//#define EXPLICIT

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using SBSDKComWrapperLib;

// The Ink namespace, which contains the Tablet PC Platform API
using Microsoft.Ink;
using System.Media;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;



namespace edu.stanford.hci.ideas
{
    /// <summary>
    /// The InkClipboard with Selection Sample Application form class
    /// </summary>
    public class WhiteboardApp : System.Windows.Forms.Form, RangerListener, AudioListener, CameraListener
    {
        // --------------- Constants ---------------

        private bool brighten = false;
        private const bool DEBUG_OUTPUT = false;

        private const bool SMARTBOARD_PRESENT = false;
        private const bool RANGER_PRESENT = false;
        private const bool NETWORK_PRESENT = true;
        private const bool CAMERA_PRESENT = true;

        private const bool ENABLE_AUTOSAVE = false;
        private const bool ENABLE_IDLE_TIMER = false;
        private const bool ENABLE_BRIGHTEN_TIMER = true;
        //private const bool SHOW_EXPLICIT_UI = true;

        // Declare constants for the pen widths used by this application.
        // Note that these constants are in high metric units (1 unit = .01mm)
        private const float ThinInkWidth = 10;
        private const float MediumInkWidth = 100;
        private const float ThickInkWidth = 200;
        private const float EraserWidth = 600;

        // Declare constant for the size of the border around selected strokes
        private const int SelectedInkWidthIncrease = 105;

        // Declare constant for the size of a lasso point
        private const int DotSize = 6;

        // Declare constant for the spacing between lasso points
        private const int DotSpacing = 7;

        // Declare constant for the padding size of the rectangle that bounds
        // the selected strokes.
        private const int SelectionRectBuffer = 8;

        // Declare constant for the lasso hit test percent (specifies how much
        // of the stroke must fall within the lasso in order to be selected).
        private const float LassoPercent = 50;

        // The index for the x and y packet values.  This index is used
        // to retrieve the lasso point data in the Packets event handler.
        private const int XPacketIndex = 0;
        private const int YPacketIndex = 1;

        public enum Tool
        {
            NONE, RED_PEN, GREEN_PEN, BLUE_PEN, BLACK_PEN, ERASER
        }

        /// <summary>
        /// Enumeration of all possible application modes:
        /// 
        ///   Ink:             The user is drawing new strokes
        ///   LassoSelection:  The user is selecting strokes with a lasso
        ///   MoveInk:         The user is moving selected strokes (this is not a menu item)
        ///   
        /// </summary>
        public enum ToolMode
        {
            Ink,
            Selection,
            MoveInk,
            StrokeErase
        }

        /// <summary>
        /// Enumeration of all possible application modes:
        /// 

        ///   
        /// </summary>
        public enum ButtonMode
        {
            Write,
            MoveSelect,
            Awareness,
            NewPage
        }


        // --------------- Fields ---------------

        // Current zone for the range scanners:
        private ZONES currentZone = ZONES.FARTHEST;

        // Declare the Ink Collector object
        private InkCollector myInkCollector = null;

        // The current application mode:  inking, selecting, or moving the selection
        private ToolMode applicationMode = ToolMode.Ink;

        private ButtonMode lastButton = ButtonMode.Write;

        // The points in the selection lasso
        private ArrayList lassoPoints = null;

        // The array of rectangle selection handles
        private PictureBox[] selectionHandles;

        // The rectangle that bounds the selected strokes
        private Rectangle selectionRect = Rectangle.Empty;

        // The strokes that were added from iDeas notebook pages
        private Strokes ambientStrokes = null;

        // The strokes that have been selected by the lasso
        private Strokes selectedStrokes = null;

        // The location where the move started (used when moving selected strokes)
        private Point startMoveLocation = Point.Empty;

        // The coordinates of the last lasso point drawn (empty if no dot has been drawn)
        private Point lastDrawnLassoDot = Point.Empty;

        // Declare the colors used in the selection lasso
        private Color dotEdgeColor = Color.White;
        private Color dotColor = SystemColors.Highlight;
        private Color connectorColor = Color.Black;

        // Declare the pens used to draw the selection lasso
        private Pen connectorPen = null;
        private Pen dotEdgePen = null;
        private Pen dotPen = null;

        // Clipboard/parsing variables
        // list of strokes when parsing xml files (balee)
        private ArrayList strokesList = null;
        // min and max x and y values of imported strokes sets
        private int minX, minY, maxX, maxY;

        // used to keep track of the last known displayed screen
        private int currentScreenNumber = 0;

        // SMART Board stuff
        private ISBSDKBaseClass2 Sbsdk;
        private _ISBSDKBaseClass2Events_Event SbsdkEvents;

        // Stroke management
        private StrokeManager strokeManager;

        // Clustering stuff
        private ClusterManager clusterManager;
        private Rectangle[] clusterBoundingBoxes;

        // Page thumbnail fields
        private ArrayList boxList = new ArrayList();
        private int thumbnailWidth = 75;
        private int thumbnailHeight = 50;

        private Image testImage;

        private float currentButtonScale = 1.0f;

        // These are the iDeas variables
        private const string TITLE = "Whiteboard";
        private Ranger ranger;
        private Camera camera;
        //private AnalogClockControl implicitClock;
        private AudioLevel audioLevel;
        private Ambient ambient;
        private readonly System.Timers.Timer AutoSaveTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer IdleTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer ShowAmbientTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer HideAmbientTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer FadeInTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer FadeOutTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer ClusterMoveTimer = new System.Timers.Timer();
        private readonly System.Timers.Timer ClockInTimer = new System.Timers.Timer();
        private int t;

        private readonly System.Timers.Timer BrightenTimer = new System.Timers.Timer();
        private Tool activeTool = Tool.BLACK_PEN;
        public delegate void AmbientDelegate();
        public delegate void AnalogClockControlDelegate();
        public delegate void ImageChangeDelegate(String url);
        public delegate void ModeChangeDelegate(ToolMode mode);

        public delegate void BrightenDelegate();

        //Wendy added this bad global temporaily to make cluster drawing work
        public bool showClusters = true;
        public int targetAlpha = 127;
        public int clusterAlpha;

        // Declare constant for the size of the hit test circle radius.
        // Note that this constant is in high metric units (1 unit = .01mm)
        private const float eraseHitTestRadius = 30; // erase hit test radius
        private const float selectHitTestRadius = 600;
        private const float clearHitTestRadius = 60000;

        // Need these for logging purposes:
        private RangerEvent mostRecentRangerEvent = null;
        private AudioEvent mostRecentAudioEvent = null;
        private CameraEvent mostRecentCameraEvent = null;

        // --------------- Constructor ---------------

        #region Standard Template Code

        private System.Windows.Forms.MenuItem miExit;
        private System.Windows.Forms.MenuItem miMainEdit;
        private System.Windows.Forms.MenuItem miCut;
        private System.Windows.Forms.MenuItem miCopy;
        private System.Windows.Forms.MenuItem miPaste;
        private System.Windows.Forms.MenuItem miInk;
        private System.Windows.Forms.MenuItem miMainFormat;
        private System.Windows.Forms.MenuItem miISF;
        private System.Windows.Forms.MenuItem miMetafile;
        private System.Windows.Forms.MenuItem miBMP;
        private System.Windows.Forms.MenuItem miEMF;
        private System.Windows.Forms.MenuItem miSInk;
        private System.Windows.Forms.MenuItem miTInk;
        private System.Windows.Forms.MenuItem miNoOLE;
        private System.Windows.Forms.MenuItem miThin;
        private System.Windows.Forms.MenuItem miMedium;
        private System.Windows.Forms.MenuItem miThick;
        private System.Windows.Forms.MenuItem miRed;
        private System.Windows.Forms.MenuItem miGreen;
        private System.Windows.Forms.MenuItem miBlue;
        private System.Windows.Forms.MenuItem miWhite;
        private System.Windows.Forms.PictureBox rightBottomHandle;
        private System.Windows.Forms.PictureBox centerBottomHandle;
        private System.Windows.Forms.PictureBox leftBottomHandle;
        private System.Windows.Forms.PictureBox rightCenterHandle;
        private System.Windows.Forms.PictureBox leftCenterHandle;
        private System.Windows.Forms.PictureBox rightTopHandle;
        private System.Windows.Forms.PictureBox centerTopHandle;
        private System.Windows.Forms.PictureBox leftTopHandle;
        private System.Windows.Forms.MenuItem miSelect;
        private System.Windows.Forms.MainMenu mainMenu;
        private System.Windows.Forms.MenuItem miEditSeparator;
        private System.Windows.Forms.MenuItem miFormatSeparator;
        private System.Windows.Forms.MenuItem miStyle;
        private System.Windows.Forms.MenuItem miWidth;
        private System.Windows.Forms.MenuItem miColor;
        private System.Windows.Forms.MenuItem miMainAction;
        #endregion

        private MenuItem menuItem2;
        private WritingPane writingPane;
        private MenuItem OpenMenu;
        private MenuItem SaveAsMenu;
        private MenuItem miClear;
        private MenuItem menuItem3;
        private MenuItem miSelectAll;
        private MenuItem miDelete;
        private MenuItem miErase;

        private AnalogClockControl implicitClock;

        private HScrollBar hScrollBar1;
        private Button newPageButton;
        private Button writeButton;
        private Button moveSelectButton;
        private Button awarenessButton;
        private Button saveButton;
        private ToolStrip inkSizeToolstrip;
        private ToolStripButton miThinToolbarButton;
        private ToolStripButton miMediumToolstripButton;
        private ToolStripButton miThickToolbarButton;
        private ToolStripButton miShowHideToolbarButton;
        private ToolStripSeparator toolStripSeparator2;
        //private TransPanel transPanel1;
        private bool showexplicitUI = true;

        private System.ComponentModel.IContainer components;

        public WhiteboardApp()
        {
            #region Standard Template Code
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();
            //this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer,true);
            this.SetStyle(
            ControlStyles.UserPaint |
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.OptimizedDoubleBuffer, true);

            #endregion

            /*FormBorderStyle = FormBorderStyle.None;
            MaximizeBox = false;
            MinimizeBox = false;
            //StartPosition = FormStartPosition.CenterScreen;
            ControlBox = false;*/
        }

        #region Standard Template Code
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        #endregion

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WhiteboardApp));
            this.mainMenu = new System.Windows.Forms.MainMenu(this.components);
            this.miMainAction = new System.Windows.Forms.MenuItem();
            this.OpenMenu = new System.Windows.Forms.MenuItem();
            this.SaveAsMenu = new System.Windows.Forms.MenuItem();
            this.menuItem2 = new System.Windows.Forms.MenuItem();
            this.miExit = new System.Windows.Forms.MenuItem();
            this.miMainEdit = new System.Windows.Forms.MenuItem();
            this.miCut = new System.Windows.Forms.MenuItem();
            this.miCopy = new System.Windows.Forms.MenuItem();
            this.miPaste = new System.Windows.Forms.MenuItem();
            this.miDelete = new System.Windows.Forms.MenuItem();
            this.menuItem3 = new System.Windows.Forms.MenuItem();
            this.miClear = new System.Windows.Forms.MenuItem();
            this.miSelectAll = new System.Windows.Forms.MenuItem();
            this.miEditSeparator = new System.Windows.Forms.MenuItem();
            this.miSelect = new System.Windows.Forms.MenuItem();
            this.miInk = new System.Windows.Forms.MenuItem();
            this.miErase = new System.Windows.Forms.MenuItem();
            this.miMainFormat = new System.Windows.Forms.MenuItem();
            this.miISF = new System.Windows.Forms.MenuItem();
            this.miMetafile = new System.Windows.Forms.MenuItem();
            this.miEMF = new System.Windows.Forms.MenuItem();
            this.miBMP = new System.Windows.Forms.MenuItem();
            this.miFormatSeparator = new System.Windows.Forms.MenuItem();
            this.miSInk = new System.Windows.Forms.MenuItem();
            this.miTInk = new System.Windows.Forms.MenuItem();
            this.miNoOLE = new System.Windows.Forms.MenuItem();
            this.miStyle = new System.Windows.Forms.MenuItem();
            this.miWidth = new System.Windows.Forms.MenuItem();
            this.miThin = new System.Windows.Forms.MenuItem();
            this.miMedium = new System.Windows.Forms.MenuItem();
            this.miThick = new System.Windows.Forms.MenuItem();
            this.miColor = new System.Windows.Forms.MenuItem();
            this.miRed = new System.Windows.Forms.MenuItem();
            this.miGreen = new System.Windows.Forms.MenuItem();
            this.miBlue = new System.Windows.Forms.MenuItem();
            this.miWhite = new System.Windows.Forms.MenuItem();
            this.rightBottomHandle = new System.Windows.Forms.PictureBox();
            this.centerBottomHandle = new System.Windows.Forms.PictureBox();
            this.leftBottomHandle = new System.Windows.Forms.PictureBox();
            this.rightCenterHandle = new System.Windows.Forms.PictureBox();
            this.leftCenterHandle = new System.Windows.Forms.PictureBox();
            this.rightTopHandle = new System.Windows.Forms.PictureBox();
            this.centerTopHandle = new System.Windows.Forms.PictureBox();
            this.leftTopHandle = new System.Windows.Forms.PictureBox();
            this.implicitClock = new edu.stanford.hci.ideas.AnalogClockControl();
            this.writingPane = new edu.stanford.hci.ideas.WritingPane();
            this.inkSizeToolstrip = new System.Windows.Forms.ToolStrip();
            this.miShowHideToolbarButton = new System.Windows.Forms.ToolStripButton();
            this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
            this.miThinToolbarButton = new System.Windows.Forms.ToolStripButton();
            this.miMediumToolstripButton = new System.Windows.Forms.ToolStripButton();
            this.miThickToolbarButton = new System.Windows.Forms.ToolStripButton();
            this.saveButton = new System.Windows.Forms.Button();
            this.writeButton = new System.Windows.Forms.Button();
            this.moveSelectButton = new System.Windows.Forms.Button();
            this.awarenessButton = new System.Windows.Forms.Button();
            this.newPageButton = new System.Windows.Forms.Button();
            this.hScrollBar1 = new System.Windows.Forms.HScrollBar();
            ((System.ComponentModel.ISupportInitialize)(this.rightBottomHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.centerBottomHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftBottomHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.rightCenterHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftCenterHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.rightTopHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.centerTopHandle)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftTopHandle)).BeginInit();
            this.writingPane.SuspendLayout();
            this.inkSizeToolstrip.SuspendLayout();
            this.SuspendLayout();
            // 
            // mainMenu
            // 
            this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miMainAction,
            this.miMainEdit,
            this.miMainFormat,
            this.miStyle});
            // 
            // miMainAction
            // 
            this.miMainAction.Index = 0;
            this.miMainAction.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.OpenMenu,
            this.SaveAsMenu,
            this.menuItem2,
            this.miExit});
            this.miMainAction.Text = "&Actions";
            // 
            // OpenMenu
            // 
            this.OpenMenu.Index = 0;
            this.OpenMenu.Shortcut = System.Windows.Forms.Shortcut.CtrlO;
            this.OpenMenu.Text = "&Open...";
            this.OpenMenu.Click += new System.EventHandler(this.miOpenMenu_Click);
            // 
            // SaveAsMenu
            // 
            this.SaveAsMenu.Index = 1;
            this.SaveAsMenu.Shortcut = System.Windows.Forms.Shortcut.CtrlS;
            this.SaveAsMenu.Text = "Save &As...";
            this.SaveAsMenu.Click += new System.EventHandler(this.miSaveAsMenu_Click);
            // 
            // menuItem2
            // 
            this.menuItem2.Index = 2;
            this.menuItem2.Text = "-";
            // 
            // miExit
            // 
            this.miExit.Index = 3;
            this.miExit.Text = "&Exit";
            this.miExit.Click += new System.EventHandler(this.miExit_Click);
            // 
            // miMainEdit
            // 
            this.miMainEdit.Index = 1;
            this.miMainEdit.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miCut,
            this.miCopy,
            this.miPaste,
            this.miDelete,
            this.menuItem3,
            this.miClear,
            this.miSelectAll,
            this.miEditSeparator,
            this.miSelect,
            this.miInk,
            this.miErase});
            this.miMainEdit.Text = "&Edit";
            this.miMainEdit.Popup += new System.EventHandler(this.miMainEdit_Popup);
            // 
            // miCut
            // 
            this.miCut.Index = 0;
            this.miCut.Shortcut = System.Windows.Forms.Shortcut.CtrlX;
            this.miCut.Text = "Cu&t";
            this.miCut.Click += new System.EventHandler(this.miCut_Click);
            // 
            // miCopy
            // 
            this.miCopy.Index = 1;
            this.miCopy.Shortcut = System.Windows.Forms.Shortcut.CtrlC;
            this.miCopy.Text = "&Copy";
            this.miCopy.Click += new System.EventHandler(this.miCopy_Click);
            // 
            // miPaste
            // 
            this.miPaste.Index = 2;
            this.miPaste.Shortcut = System.Windows.Forms.Shortcut.CtrlV;
            this.miPaste.Text = "&Paste";
            this.miPaste.Click += new System.EventHandler(this.miPaste_Click);
            // 
            // miDelete
            // 
            this.miDelete.Index = 3;
            this.miDelete.Shortcut = System.Windows.Forms.Shortcut.Del;
            this.miDelete.Text = "Delete";
            this.miDelete.Visible = false;
            this.miDelete.Click += new System.EventHandler(this.miDelete_Click);
            // 
            // menuItem3
            // 
            this.menuItem3.Index = 4;
            this.menuItem3.Text = "-";
            // 
            // miClear
            // 
            this.miClear.Index = 5;
            this.miClear.Text = "&Clear";
            this.miClear.Click += new System.EventHandler(this.miClear_Click);
            // 
            // miSelectAll
            // 
            this.miSelectAll.Index = 6;
            this.miSelectAll.Shortcut = System.Windows.Forms.Shortcut.CtrlA;
            this.miSelectAll.Text = "Select A&ll";
            this.miSelectAll.Click += new System.EventHandler(this.miSelectAllMenu_Click);
            // 
            // miEditSeparator
            // 
            this.miEditSeparator.Index = 7;
            this.miEditSeparator.Text = "-";
            // 
            // miSelect
            // 
            this.miSelect.Index = 8;
            this.miSelect.RadioCheck = true;
            this.miSelect.Text = "&Select";
            this.miSelect.Click += new System.EventHandler(this.miSelect_Click);
            // 
            // miInk
            // 
            this.miInk.Checked = true;
            this.miInk.Index = 9;
            this.miInk.RadioCheck = true;
            this.miInk.Text = "&Ink";
            this.miInk.Click += new System.EventHandler(this.miInk_Click);
            // 
            // miErase
            // 
            this.miErase.Index = 10;
            this.miErase.RadioCheck = true;
            this.miErase.Text = "&Erase";
            this.miErase.Click += new System.EventHandler(this.miErase_Click);
            // 
            // miMainFormat
            // 
            this.miMainFormat.Index = 2;
            this.miMainFormat.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miISF,
            this.miMetafile,
            this.miEMF,
            this.miBMP,
            this.miFormatSeparator,
            this.miSInk,
            this.miTInk,
            this.miNoOLE});
            this.miMainFormat.Text = "&Format";
            // 
            // miISF
            // 
            this.miISF.Checked = true;
            this.miISF.Index = 0;
            this.miISF.Text = "&Ink Serialized Format (ISF)";
            this.miISF.Click += new System.EventHandler(this.miISF_Click);
            // 
            // miMetafile
            // 
            this.miMetafile.Checked = true;
            this.miMetafile.Index = 1;
            this.miMetafile.Text = "&Metafile";
            this.miMetafile.Click += new System.EventHandler(this.miMetafile_Click);
            // 
            // miEMF
            // 
            this.miEMF.Checked = true;
            this.miEMF.Index = 2;
            this.miEMF.Text = "&Enhanced Metafile";
            this.miEMF.Click += new System.EventHandler(this.miEMF_Click);
            // 
            // miBMP
            // 
            this.miBMP.Checked = true;
            this.miBMP.Index = 3;
            this.miBMP.Text = "&Bitmap";
            this.miBMP.Click += new System.EventHandler(this.miBMP_Click);
            // 
            // miFormatSeparator
            // 
            this.miFormatSeparator.Index = 4;
            this.miFormatSeparator.Text = "-";
            // 
            // miSInk
            // 
            this.miSInk.Index = 5;
            this.miSInk.RadioCheck = true;
            this.miSInk.Text = "&Sketch Ink";
            this.miSInk.Click += new System.EventHandler(this.miSInk_Click);
            // 
            // miTInk
            // 
            this.miTInk.Checked = true;
            this.miTInk.Index = 6;
            this.miTInk.RadioCheck = true;
            this.miTInk.Text = "&Text Ink";
            this.miTInk.Click += new System.EventHandler(this.miTInk_Click);
            // 
            // miNoOLE
            // 
            this.miNoOLE.Index = 7;
            this.miNoOLE.RadioCheck = true;
            this.miNoOLE.Text = "&No OLE Object Format";
            this.miNoOLE.Click += new System.EventHandler(this.miNoOLE_Click);
            // 
            // miStyle
            // 
            this.miStyle.Index = 3;
            this.miStyle.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miWidth,
            this.miColor});
            this.miStyle.Text = "&Style";
            // 
            // miWidth
            // 
            this.miWidth.Index = 0;
            this.miWidth.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miThin,
            this.miMedium,
            this.miThick});
            this.miWidth.Text = "&Width";
            // 
            // miThin
            // 
            this.miThin.Index = 0;
            this.miThin.Text = "Thin";
            this.miThin.Click += new System.EventHandler(this.miThin_Click);
            // 
            // miMedium
            // 
            this.miMedium.Index = 1;
            this.miMedium.Text = "Medium";
            this.miMedium.Click += new System.EventHandler(this.miMedium_Click);
            // 
            // miThick
            // 
            this.miThick.Index = 2;
            this.miThick.Text = "Thick";
            this.miThick.Click += new System.EventHandler(this.miThick_Click);
            // 
            // miColor
            // 
            this.miColor.Index = 1;
            this.miColor.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            this.miRed,
            this.miGreen,
            this.miBlue,
            this.miWhite});
            this.miColor.Text = "&Color";
            // 
            // miRed
            // 
            this.miRed.Index = 0;
            this.miRed.Text = "Red";
            this.miRed.Click += new System.EventHandler(this.miRed_Click);
            // 
            // miGreen
            // 
            this.miGreen.Index = 1;
            this.miGreen.Text = "Green";
            this.miGreen.Click += new System.EventHandler(this.miGreen_Click);
            // 
            // miBlue
            // 
            this.miBlue.Index = 2;
            this.miBlue.Text = "Blue";
            this.miBlue.Click += new System.EventHandler(this.miBlue_Click);
            // 
            // miWhite
            // 
            this.miWhite.Index = 3;
            this.miWhite.Text = "White";
            this.miWhite.Click += new System.EventHandler(this.miWhite_Click);
            // 
            // rightBottomHandle
            // 
            this.rightBottomHandle.BackColor = System.Drawing.Color.Gray;
            this.rightBottomHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.rightBottomHandle.Image = ((System.Drawing.Image)(resources.GetObject("rightBottomHandle.Image")));
            this.rightBottomHandle.Location = new System.Drawing.Point(281, 256);
            this.rightBottomHandle.Name = "rightBottomHandle";
            this.rightBottomHandle.Size = new System.Drawing.Size(11, 11);
            this.rightBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.rightBottomHandle.TabIndex = 20;
            this.rightBottomHandle.TabStop = false;
            this.rightBottomHandle.Visible = false;
            // 
            // centerBottomHandle
            // 
            this.centerBottomHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.centerBottomHandle.Image = ((System.Drawing.Image)(resources.GetObject("centerBottomHandle.Image")));
            this.centerBottomHandle.Location = new System.Drawing.Point(137, 256);
            this.centerBottomHandle.Name = "centerBottomHandle";
            this.centerBottomHandle.Size = new System.Drawing.Size(9, 9);
            this.centerBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.centerBottomHandle.TabIndex = 19;
            this.centerBottomHandle.TabStop = false;
            this.centerBottomHandle.Visible = false;
            // 
            // leftBottomHandle
            // 
            this.leftBottomHandle.BackColor = System.Drawing.Color.Gray;
            this.leftBottomHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.leftBottomHandle.Image = ((System.Drawing.Image)(resources.GetObject("leftBottomHandle.Image")));
            this.leftBottomHandle.Location = new System.Drawing.Point(1, 256);
            this.leftBottomHandle.Name = "leftBottomHandle";
            this.leftBottomHandle.Size = new System.Drawing.Size(11, 11);
            this.leftBottomHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.leftBottomHandle.TabIndex = 18;
            this.leftBottomHandle.TabStop = false;
            this.leftBottomHandle.Visible = false;
            // 
            // rightCenterHandle
            // 
            this.rightCenterHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.rightCenterHandle.Image = ((System.Drawing.Image)(resources.GetObject("rightCenterHandle.Image")));
            this.rightCenterHandle.Location = new System.Drawing.Point(281, 128);
            this.rightCenterHandle.Name = "rightCenterHandle";
            this.rightCenterHandle.Size = new System.Drawing.Size(9, 9);
            this.rightCenterHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.rightCenterHandle.TabIndex = 17;
            this.rightCenterHandle.TabStop = false;
            this.rightCenterHandle.Visible = false;
            // 
            // leftCenterHandle
            // 
            this.leftCenterHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.leftCenterHandle.Image = ((System.Drawing.Image)(resources.GetObject("leftCenterHandle.Image")));
            this.leftCenterHandle.Location = new System.Drawing.Point(1, 128);
            this.leftCenterHandle.Name = "leftCenterHandle";
            this.leftCenterHandle.Size = new System.Drawing.Size(9, 9);
            this.leftCenterHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.leftCenterHandle.TabIndex = 16;
            this.leftCenterHandle.TabStop = false;
            this.leftCenterHandle.Visible = false;
            // 
            // rightTopHandle
            // 
            this.rightTopHandle.BackColor = System.Drawing.Color.Gray;
            this.rightTopHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.rightTopHandle.Image = ((System.Drawing.Image)(resources.GetObject("rightTopHandle.Image")));
            this.rightTopHandle.Location = new System.Drawing.Point(281, 0);
            this.rightTopHandle.Name = "rightTopHandle";
            this.rightTopHandle.Size = new System.Drawing.Size(11, 11);
            this.rightTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.rightTopHandle.TabIndex = 15;
            this.rightTopHandle.TabStop = false;
            this.rightTopHandle.Visible = false;
            // 
            // centerTopHandle
            // 
            this.centerTopHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.centerTopHandle.Image = ((System.Drawing.Image)(resources.GetObject("centerTopHandle.Image")));
            this.centerTopHandle.Location = new System.Drawing.Point(137, 0);
            this.centerTopHandle.Name = "centerTopHandle";
            this.centerTopHandle.Size = new System.Drawing.Size(9, 9);
            this.centerTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.centerTopHandle.TabIndex = 14;
            this.centerTopHandle.TabStop = false;
            this.centerTopHandle.Visible = false;
            // 
            // leftTopHandle
            // 
            this.leftTopHandle.BackColor = System.Drawing.Color.Gray;
            this.leftTopHandle.Cursor = System.Windows.Forms.Cursors.Default;
            this.leftTopHandle.Image = ((System.Drawing.Image)(resources.GetObject("leftTopHandle.Image")));
            this.leftTopHandle.Location = new System.Drawing.Point(1, 0);
            this.leftTopHandle.Name = "leftTopHandle";
            this.leftTopHandle.Size = new System.Drawing.Size(11, 11);
            this.leftTopHandle.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.leftTopHandle.TabIndex = 13;
            this.leftTopHandle.TabStop = false;
            this.leftTopHandle.Visible = false;
            // 
            // implicitClock
            // 
            this.implicitClock.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.implicitClock.Draw1MinuteTicks = true;
            this.implicitClock.Draw5MinuteTicks = true;
            this.implicitClock.HourHandColor = System.Drawing.Color.LightSteelBlue;
            this.implicitClock.Location = new System.Drawing.Point(679, 3);
            this.implicitClock.MinuteHandColor = System.Drawing.Color.BlueViolet;
            this.implicitClock.Name = "implicitClock";
            this.implicitClock.SecondHandColor = System.Drawing.Color.Red;
            this.implicitClock.Size = new System.Drawing.Size(112, 112);
            this.implicitClock.TabIndex = 0;
            this.implicitClock.TicksColor = System.Drawing.Color.LightGray;
            this.implicitClock.Click += new System.EventHandler(this.implicitClock.Toggle);
            // 
            // writingPane
            // 
            this.writingPane.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.writingPane.BackColor = System.Drawing.Color.Transparent;
            this.writingPane.Controls.Add(this.inkSizeToolstrip);
            this.writingPane.Controls.Add(this.saveButton);
            this.writingPane.Controls.Add(this.writeButton);
            this.writingPane.Controls.Add(this.moveSelectButton);
            this.writingPane.Controls.Add(this.awarenessButton);
            this.writingPane.Controls.Add(this.newPageButton);
            this.writingPane.Controls.Add(this.hScrollBar1);
            this.writingPane.Location = new System.Drawing.Point(0, 0);
            this.writingPane.Name = "writingPane";
            this.writingPane.Size = new System.Drawing.Size(794, 463);
            this.writingPane.TabIndex = 21;
            this.writingPane.Text = "Whiteboard";
            this.writingPane.MouseDown += new System.Windows.Forms.MouseEventHandler(this.WritingPane_MouseDown);
            this.writingPane.MouseMove += new System.Windows.Forms.MouseEventHandler(this.WritingPane_MouseMove);
            this.writingPane.Paint += new System.Windows.Forms.PaintEventHandler(this.WritingPane_Paint);
            this.writingPane.MouseUp += new System.Windows.Forms.MouseEventHandler(this.WritingPane_MouseUp);
            // 
            // inkSizeToolstrip
            // 
            this.inkSizeToolstrip.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.inkSizeToolstrip.AutoSize = false;
            this.inkSizeToolstrip.Dock = System.Windows.Forms.DockStyle.None;
            this.inkSizeToolstrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.miShowHideToolbarButton,
            this.toolStripSeparator2,
            this.miThinToolbarButton,
            this.miMediumToolstripButton,
            this.miThickToolbarButton});
            this.inkSizeToolstrip.Location = new System.Drawing.Point(1, 421);
            this.inkSizeToolstrip.Name = "inkSizeToolstrip";
            this.inkSizeToolstrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
            this.inkSizeToolstrip.ShowItemToolTips = false;
            this.inkSizeToolstrip.Size = new System.Drawing.Size(227, 25);
            this.inkSizeToolstrip.TabIndex = 104;
            this.inkSizeToolstrip.Text = "Ink Size";
            this.inkSizeToolstrip.Visible = false;
            // 
            // miShowHideToolbarButton
            // 
            this.miShowHideToolbarButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.miShowHideToolbarButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.miShowHideToolbarButton.Name = "miShowHideToolbarButton";
            this.miShowHideToolbarButton.Size = new System.Drawing.Size(77, 22);
            this.miShowHideToolbarButton.Text = "Hide Ink Sizes";
            this.miShowHideToolbarButton.Click += new System.EventHandler(this.miShowHideToolbarButton_Click);
            // 
            // toolStripSeparator2
            // 
            this.toolStripSeparator2.Name = "toolStripSeparator2";
            this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
            // 
            // miThinToolbarButton
            // 
            this.miThinToolbarButton.CheckOnClick = true;
            this.miThinToolbarButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.miThinToolbarButton.Name = "miThinToolbarButton";
            this.miThinToolbarButton.Size = new System.Drawing.Size(31, 22);
            this.miThinToolbarButton.Text = "Thin";
            this.miThinToolbarButton.ToolTipText = "Thin Ink";
            this.miThinToolbarButton.Click += new System.EventHandler(this.miThin_Click);
            // 
            // miMediumToolstripButton
            // 
            this.miMediumToolstripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.miMediumToolstripButton.Name = "miMediumToolstripButton";
            this.miMediumToolstripButton.Size = new System.Drawing.Size(47, 22);
            this.miMediumToolstripButton.Text = "Medium";
            this.miMediumToolstripButton.ToolTipText = "Medium Ink";
            this.miMediumToolstripButton.Click += new System.EventHandler(this.miMedium_Click);
            // 
            // miThickToolbarButton
            // 
            this.miThickToolbarButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.miThickToolbarButton.Name = "miThickToolbarButton";
            this.miThickToolbarButton.Size = new System.Drawing.Size(35, 22);
            this.miThickToolbarButton.Text = "Thick";
            this.miThickToolbarButton.ToolTipText = "Thick Ink";
            this.miThickToolbarButton.Click += new System.EventHandler(this.miThick_Click);
            // 
            // saveButton
            // 
            this.saveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.saveButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.saveButton.Location = new System.Drawing.Point(695, 385);
            this.saveButton.Name = "saveButton";
            this.saveButton.Size = new System.Drawing.Size(85, 47);
            this.saveButton.TabIndex = 105;
            this.saveButton.TabStop = false;
            this.saveButton.Text = "Save";
            this.saveButton.UseVisualStyleBackColor = true;
            this.saveButton.Visible = false;
            this.saveButton.Click += new System.EventHandler(this.miSaveAsMenu_Click);
            // 
            // writeButton
            // 
            this.writeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.writeButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.writeButton.Location = new System.Drawing.Point(695, 185);
            this.writeButton.Name = "writeButton";
            this.writeButton.Size = new System.Drawing.Size(85, 47);
            this.writeButton.TabIndex = 103;
            this.writeButton.TabStop = false;
            this.writeButton.Text = "Write";
            this.writeButton.UseVisualStyleBackColor = true;
            this.writeButton.Visible = false;
            this.writeButton.Click += new System.EventHandler(this.writeButton_Click);
            // 
            // moveSelectButton
            // 
            this.moveSelectButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.moveSelectButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.moveSelectButton.Location = new System.Drawing.Point(695, 235);
            this.moveSelectButton.Name = "moveSelectButton";
            this.moveSelectButton.Size = new System.Drawing.Size(85, 47);
            this.moveSelectButton.TabIndex = 102;
            this.moveSelectButton.TabStop = false;
            this.moveSelectButton.Text = "Move/Select";
            this.moveSelectButton.UseVisualStyleBackColor = true;
            this.moveSelectButton.Visible = false;
            this.moveSelectButton.Click += new System.EventHandler(this.moveSelectButton_Click);
            // 
            // awarenessButton
            // 
            this.awarenessButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.awarenessButton.Enabled = false;
            this.awarenessButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.awarenessButton.Location = new System.Drawing.Point(695, 285);
            this.awarenessButton.Name = "awarenessButton";
            this.awarenessButton.Size = new System.Drawing.Size(85, 47);
            this.awarenessButton.TabIndex = 101;
            this.awarenessButton.TabStop = false;
            this.awarenessButton.Text = "Awareness Display";
            this.awarenessButton.UseVisualStyleBackColor = true;
            this.awarenessButton.Visible = false;
            this.awarenessButton.Click += new System.EventHandler(this.awarenessButton_Click);
            // 
            // newPageButton
            // 
            this.newPageButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.newPageButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.newPageButton.Location = new System.Drawing.Point(695, 335);
            this.newPageButton.Name = "newPageButton";
            this.newPageButton.Size = new System.Drawing.Size(85, 47);
            this.newPageButton.TabIndex = 100;
            this.newPageButton.TabStop = false;
            this.newPageButton.Text = "New Page";
            this.newPageButton.UseVisualStyleBackColor = true;
            this.newPageButton.Visible = false;
            this.newPageButton.Click += new System.EventHandler(this.newPageButton_Click);
            // 
            // hScrollBar1
            // 
            this.hScrollBar1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.hScrollBar1.LargeChange = 1;
            this.hScrollBar1.Location = new System.Drawing.Point(0, 446);
            this.hScrollBar1.Maximum = 0;
            this.hScrollBar1.Name = "hScrollBar1";
            this.hScrollBar1.Size = new System.Drawing.Size(794, 17);
            this.hScrollBar1.TabIndex = 0;
            this.hScrollBar1.ValueChanged += new System.EventHandler(this.ScrollValueChanged);
            // 
            // WhiteboardApp
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.BackColor = System.Drawing.Color.White;
            this.ClientSize = new System.Drawing.Size(792, 461);
            this.Controls.Add(this.rightBottomHandle);
            this.Controls.Add(this.centerBottomHandle);
            this.Controls.Add(this.leftBottomHandle);
            this.Controls.Add(this.rightCenterHandle);
            this.Controls.Add(this.leftCenterHandle);
            this.Controls.Add(this.rightTopHandle);
            this.Controls.Add(this.centerTopHandle);
            this.Controls.Add(this.leftTopHandle);
            this.Controls.Add(this.implicitClock);
            this.Controls.Add(this.writingPane);
            this.Menu = this.mainMenu;
            this.Name = "WhiteboardApp";
            this.Text = "Whiteboard";
            this.Closed += new System.EventHandler(this.WhiteboardApp_Close);
            this.Load += new System.EventHandler(this.WhiteboardApp_Load);
            ((System.ComponentModel.ISupportInitialize)(this.rightBottomHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.centerBottomHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftBottomHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.rightCenterHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftCenterHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.rightTopHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.centerTopHandle)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.leftTopHandle)).EndInit();
            this.writingPane.ResumeLayout(false);
            this.inkSizeToolstrip.ResumeLayout(false);
            this.inkSizeToolstrip.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }
        #endregion

        #region Standard Template Code
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new WhiteboardApp());
        }
        #endregion

        //public void KeyPressed(object sender, KeyPressEventArgs e)
        protected override bool ProcessDialogKey(Keys keyData)
        {
            Console.WriteLine("Pressed " + keyData + "!");
            if (keyData.Equals(Keys.F2))
            {
                currentButtonScale = 1.5f - currentButtonScale;
                ScaleButtons(currentButtonScale);
                return true;
            }
            if (keyData.Equals(Keys.Oemcomma))
            {
                MoveAllStrokes(-50, 0);
                return true;
            }
            if (keyData.Equals(Keys.OemPeriod))
            {
                MoveAllStrokes(50, 0);
                return true;
            }
            if (keyData.Equals(Keys.Oemcomma | Keys.Shift))
            {
                PreviousScreen();
                return true;
            }
            if (keyData.Equals(Keys.OemPeriod | Keys.Shift))
            {
                NextScreen();
                return true;
            }
            if (keyData.Equals(Keys.A))
            {
                if (!ambient.neverShow)
                {
                    ambient.neverShow = true;
                    AmbientDelegate d = new AmbientDelegate(ambient.Hide);
                    writingPane.Invoke(d);
                }
                else
                {
                    ambient.neverShow = false;
                    AmbientDelegate d = new AmbientDelegate(ambient.Show);
                    writingPane.Invoke(d);
                }
                return true;
            }
            if (keyData.Equals(Keys.L))
            {
                LoadAnotoXML(File.Open("C:\\Documents and Settings\\dakers.GFX\\Desktop\\page_5.xml", FileMode.Open), 0);
                return true;
            }
            if (keyData.Equals(Keys.M))
            {
                Point center = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
                MoveClustersFromArea(center, 8000);
                writingPane.Invalidate();
                return true;
            }
            if (keyData.Equals(Keys.S))
            {
                StartClockInTimer(); 
  
                //Console.WriteLine("Clock width =" + implicitClock.Width);
                return true;
            }
            if (keyData.Equals(Keys.T))
            {
                ImageChangeDelegate d = new ImageChangeDelegate(ambient.SetImage);
                writingPane.Invoke(d, new Object[] { "http://hci.stanford.edu/ideas/images/collage.jpg" });
                return true;
            }
            if (keyData.Equals(Keys.D1))
            {
                SimulateRangerEvent(ZONES.CLOSEST);
                return true;
            }
            if (keyData.Equals(Keys.D2))
            {
                SimulateRangerEvent(ZONES.CLOSE);
                return true;
            }
            if (keyData.Equals(Keys.D3))
            {
                SimulateRangerEvent(ZONES.FAR);
                return true;
            }
            if (keyData.Equals(Keys.D4))
            {
                SimulateRangerEvent(ZONES.FARTHEST);
                return true;
            }
            if (keyData.Equals(Keys.P))
            {
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.PEN_GRABBED,
                    currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
                RestorePen();
                ClearAmbientStrokes();
                writingPane.Invalidate();
                return true;
            }
            if (keyData.Equals(Keys.N))
            {
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.PEN_RELEASED, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
                OnNoTool();
                return true;
            }

            if (keyData.Equals(Keys.C))
            {
                if (!implicitClock.Visible)
                {
                    StartClockInTimer();
                }
                else implicitClock.Toggle();
                return true;
            }
            if (keyData.Equals(Keys.B))
            {
                brighten = !brighten;
                writingPane.Invalidate();
            }
            if (keyData.Equals(Keys.E))
            {
                ToggleExplicitUI();
                writingPane.Invalidate();
            }
            return false;
        }

        public void StartHideAmbientTimer()
        {
            StopShowAmbientTimer();
            if (!HideAmbientTimer.Enabled)
            {
                Console.WriteLine("started HideAmbient timer!");
                HideAmbientTimer.Start();
            }
        }

        public void StopHideAmbientTimer()
        {
            if (HideAmbientTimer.Enabled)
            {
                Console.WriteLine("stopped HideAmbient timer!");
                HideAmbientTimer.Stop();
            }
        }

        public void OnHideAmbientTick(object sender, EventArgs e)
        {

            Console.WriteLine("tick: HideAmbient timer!");
            //call on the function that changes the background.
            AmbientDelegate d = new AmbientDelegate(ambient.Hide);
            writingPane.Invoke(d);
        }

        public void StartShowAmbientTimer()
        {
            StopHideAmbientTimer();
            if (!ShowAmbientTimer.Enabled)
            {
                Console.WriteLine("started ShowAmbient timer!");
                ShowAmbientTimer.Start();
            }
        }

        public void StopShowAmbientTimer()
        {
            if (ShowAmbientTimer.Enabled)
            {
                Console.WriteLine("stopped ShowAmbient timer!");
                ShowAmbientTimer.Stop();
            }
        }

        public void OnShowAmbientTick(object sender, EventArgs e)
        {

            //Console.WriteLine("tick: ShowAmbient timer!");
            AmbientDelegate d = new AmbientDelegate(ambient.Show);
            writingPane.Invoke(d);
        }

        public void OnAutoSaveTick(object sender, EventArgs e)
        {
            Stream myStream = null;
            Console.WriteLine("tick: autosave timer! (" + String.Format("{0:yyyyMMddHHmmss}", DateTime.Now) + ".isf)");
            try
            {
                /// Attempt to Open the file with read only permission
                myStream = System.IO.File.Open("autosave" + String.Format("{0:yyyyMMddHHmmss}", DateTime.Now) + ".isf", FileMode.Create, FileAccess.ReadWrite);
                if (myStream != null)
                {
                    StrokeUtil.SaveISF(myInkCollector, myStream);
                }
                else
                {
                    // Throw an exception if a null pointer is returned for the stream
                    throw new IOException();
                }
            }
            catch (IOException /*ioe*/)
            {
                MessageBox.Show("Autosave error!");
            }
            finally
            {
                // Close the stream in the finally clause so it
                // is always reached, regardless of whether an 
                // exception occurs.
                if (null != myStream)
                {
                    myStream.Close();
                }
            }
        }
   
        public void StartIdleTimer()
        {
            if (!IdleTimer.Enabled && ENABLE_IDLE_TIMER)
            {
                Console.WriteLine("started idle timer!");
                IdleTimer.Start();
            }
        }

        public void StopIdleTimer()
        {
            if (IdleTimer.Enabled && ENABLE_IDLE_TIMER)
            {
                Console.WriteLine("stopped idle timer!");
                IdleTimer.Stop();
            }
        }
        public void StartBrightenTimer()
        {
            if (!BrightenTimer.Enabled && ENABLE_BRIGHTEN_TIMER)
            {
                Console.WriteLine("started Brighten timer!");
                BrightenTimer.Start();
            }
        }

        public void StopBrightenTimer()
        {
            if (BrightenTimer.Enabled && ENABLE_BRIGHTEN_TIMER)
            {
                Console.WriteLine("stopped Brighten timer!");
                BrightenTimer.Stop();
            }
        }

        public void OnIdleTick(object sender, EventArgs e)
        {

            Console.WriteLine("tick: idle timer!");
            AmbientDelegate d = new AmbientDelegate(ambient.Show);
            StartShowAmbientTimer();
            writingPane.Invoke(d);
        }


        public void OnBrightenTick(object sender, EventArgs e)
        {

            //Console.WriteLine("tick: brighten timer!");
            if (mostRecentCameraEvent != null)
            {
                TimeSpan elapsedTime = System.DateTime.Now - mostRecentCameraEvent.getTimeStamp();
                if (elapsedTime.TotalSeconds > 2 && currentZone != ZONES.CLOSEST)
                {
                    //BrightenDelegate d = new BrightenDelegate(transPanel1.Dim);
                    //transPanel1.Invoke(d);
                    brighten = false;
                    this.writingPane.Invalidate();
                }
                
            }
            //this.transPanel1.UpdateVisibility();
        }

        public void StartClockInTimer()
        {
            if (implicitClock.Visible) return;
            else ClockInTimer.Start();
            if (implicitClock.InvokeRequired) {    
                AnalogClockControlDelegate d = new AnalogClockControlDelegate(StartClockInTimer);
                this.Invoke(d);
            }
            else
            {
 
                implicitClock.Location = new Point (implicitClock.Location.X+(implicitClock.Width/2),implicitClock.Location.Y+(implicitClock.Height/2));
                implicitClock.Height = 1;   
                implicitClock.Show();
                t = 1;
            }
        }

        public void OnClockInTick(object sender, EventArgs e)
        {
            if (implicitClock.InvokeRequired)
            {
                AnalogClockControlDelegate d = new AnalogClockControlDelegate(OnClockInTick);
                this.Invoke(d);
            }
            else
            {
                if (implicitClock.Height < 150)
                {
                    implicitClock.Location = new Point(implicitClock.Location.X + implicitClock.Width, implicitClock.Location.Y + implicitClock.Height);
                    implicitClock.Height = implicitClock.Height * 2;
                    Console.WriteLine("t="+t++);
                   
                }
                else ClockInTimer.Stop();
            }
           
        }
        public void OnClockInTick()
        {
            if (implicitClock.InvokeRequired)
            {
                AnalogClockControlDelegate d = new AnalogClockControlDelegate(OnClockInTick);
                this.Invoke(d);
            }
            else
            {
                if (t < 8)
                {
                    implicitClock.Location = new Point(implicitClock.Location.X - implicitClock.Width/2, implicitClock.Location.Y - implicitClock.Height/2);
                    implicitClock.Height = implicitClock.Height * 2;
                    t++;
                    //Console.WriteLine("clock location:" + implicitClock.Location.X + "," + implicitClock.Location.Y);
                }
                else if (implicitClock.Height > 112)
                {
                    implicitClock.Location = new Point(implicitClock.Location.X + 1, implicitClock.Location.Y + 1);
                    implicitClock.Height = implicitClock.Height - 2;
                    t++;
                   // Console.WriteLine("clock location:" + implicitClock.Location.X + "," + implicitClock.Location.Y);
                }
                else
                {
                    implicitClock.Location = new Point(implicitClock.Location.X - 1, implicitClock.Location.Y - 1);
                   // Console.WriteLine("clock location:" + implicitClock.Location.X + "," + implicitClock.Location.Y);
                    ClockInTimer.Stop();
                }
            }
        }

        public void StartFadeInTimer()
        {
            // fade in outlines of the clusters (as rectangles)...
            // this starts the timer, setting the alpha to zero.

            if ((!showClusters) || (clusterAlpha > targetAlpha)) return;
            StopFadeOutTimer();
            if (!FadeInTimer.Enabled)
            {
                //Console.WriteLine("started FadeIn timer!");
                FadeInTimer.Start();
                clusterAlpha = 0;
            }
        }

        
        public void StopFadeInTimer()
        {
            // fade *out* outlines of the clusters (as rectangles)...
            // this stops the timer, presumably after we've reached the target
            // alpha value.

            if (FadeInTimer.Enabled)
            {
                //Console.WriteLine("stopped FadeIn timer!");
                FadeInTimer.Stop();
            }
        }

        public void OnFadeInTick(object sender, EventArgs e)
        {
            // For each tick as the cluster boundaries are fading in,
            // the alpha is updated. When we reach the target alpha value,
            // stop the timer.
            //Console.WriteLine("Fade in tick.");

            if ((!showClusters) || (clusterAlpha > targetAlpha))
            {
                StopFadeInTimer();
                return;
            }
            //Console.WriteLine("fadein, alpha" + clusterAlpha);
            writingPane.Invalidate();
            clusterAlpha += ((targetAlpha - clusterAlpha) / 2) + 1;
        }

        public void StartFadeOutTimer()
        {
            // start fading out of cluster boundaries.

            if (clusterAlpha <= 0) return;
            StopFadeInTimer();
            if (!FadeOutTimer.Enabled)
            {
                FadeOutTimer.Start();
                clusterAlpha = targetAlpha;
            }
        }

        public void StopFadeOutTimer()
        {
            // stop fading out of cluster boundaries.

            if (FadeOutTimer.Enabled)
            {
                FadeOutTimer.Stop();
            }
        }

        public void OnFadeOutTick(object sender, EventArgs e)
        {
            //Console.WriteLine("Fade out tick.");

            // on each tick, lower the cluster boundary alpha value,
            // until it reaches 0.

            //Console.WriteLine("fadeout, alpha" + clusterAlpha);
            if (clusterAlpha <= 0)
            {
                StopFadeOutTimer();
                return;
            }
            writingPane.Invalidate();
            clusterAlpha = (clusterAlpha / 2) - 1;
            clusterAlpha = (clusterAlpha < 0) ? 0 : clusterAlpha;
        }

        //should check there are clusters before starting this!
        public void StartClusterMoveTimer()
        {
            if (!ClusterMoveTimer.Enabled)
            {
                Console.WriteLine("started timer!");
                ClusterMoveTimer.Start();
            }
        }
        public void StopClusterMoveTimer()
        {
            if (ClusterMoveTimer.Enabled)
            {
                Console.WriteLine("stopped timer!");
                ClusterMoveTimer.Stop();
            }
        }

        public void OnClusterMoveTick(object sender, EventArgs e)
        {
            Console.WriteLine("CMTick");
            Point center = new Point(ClientSize.Width, ClientSize.Height);
            if (MoveClustersFromArea(center, 8000) == 0) StopClusterMoveTimer();
            writingPane.Invalidate();
        }

        private void MoveAllStrokes(int deltaX, int deltaY)
        {
            Point p = new Point(deltaX, deltaY);
            myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref p);
            MoveAllStrokesInInkSpace(p.X, p.Y);
        }

        private void MoveAllStrokesInInkSpace(int deltaX, int deltaY)
        {
            Strokes strokes = myInkCollector.Ink.Strokes;
            foreach (Stroke s in strokes)
            {
                s.Move(deltaX, deltaY);
            }
            RecalculateBoundingBoxes();
            writingPane.Invalidate();
        }

        public void MoveClusterFromPoint(Strokes cluster, Point pt)
        {
            int increment = 30;
            Point clusterCenter = GetClusterCenter(cluster);
            double deltaX = pt.X - clusterCenter.X;
            double deltaY = pt.Y - clusterCenter.Y;
            float moveY = (float)Math.Sin(Math.Atan(deltaY / deltaX)) * increment;
            float moveX = (float)Math.Cos(Math.Atan(deltaY / deltaX)) * increment;

            if (deltaX >= 0)
            {
                cluster.Move(-moveX, -moveY);
            }
            else cluster.Move(moveX, moveY);
            Console.WriteLine("moving cluster " + moveX + ", " + moveY);
            return;

        }

        private int MoveClustersFromArea(Point center, int radius)
        {
            myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref center);
            Strokes strokesInArea = myInkCollector.Ink.HitTest(center, radius);
            Console.WriteLine("# strokes: " + strokesInArea.Count);
            foreach (Stroke s in strokesInArea)
            {
                Strokes clusterToMove = clusterManager.GetClusterForStroke(s);
                MoveClusterFromPoint(clusterToMove, center);
            }
            strokesInArea = myInkCollector.Ink.HitTest(center, radius);
            RecalculateBoundingBoxes();
            return (strokesInArea.Count);
        }

        private Point GetClusterCenter(Strokes cluster)
        {
            Point ptCenter = new Point();
            Rectangle clusterRect = new Rectangle();
            clusterRect = cluster.GetBoundingBox();
            ptCenter = clusterRect.Location;
            ptCenter.X += clusterRect.Width;
            ptCenter.Y += clusterRect.Height;
            return ptCenter;
        }

        public RangerEvent GetMostRecentRangerEvent()
        {
            if (mostRecentRangerEvent == null)
            {
                mostRecentRangerEvent = new RangerEvent();
            }
            return mostRecentRangerEvent;
        }

        public AudioEvent GetMostRecentAudioEvent()
        {
            if (mostRecentAudioEvent == null)
            {
                mostRecentAudioEvent = new AudioEvent();
            }
            return mostRecentAudioEvent;
        }

        public CameraEvent GetMostRecentCameraEvent()
        {
            if (mostRecentCameraEvent == null)
            {
                mostRecentCameraEvent = new CameraEvent();
            }
            return mostRecentCameraEvent;
        }

        public PEN_STATUS GetPenStatus()
        {
            PEN_STATUS penStatus;
            if (activeTool != Tool.NONE && activeTool != Tool.ERASER)
            {
                penStatus = PEN_STATUS.IN_HAND;
            }
            else
            {
                penStatus = PEN_STATUS.IN_TRAY;
            }
            return penStatus;
        }


        public void enteredZone(RangerEvent e)
        {
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.RANGE_MODE_CHANGED, e.getCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
            // handles transitions between the various ranges the user can
            // be from the board:
            //
            // entering farthest: 
            //   - start the idle timer (cycles images)
            //   - fade out cluster boundaries if necessary
            //
            // entering far:
            //   - fade out cluster boundaries if necessary
            // 
            // entering close:
            //   - fade in cluster boundaries
            //   - stop the idle timer (no more cycling images)
            //   - start moving clusters out of the way (?)
            //
            // entering closest:
            //   - fade out cluster boundaries
            //   - set mouse cursor appropriately based on mode.

            currentZone = e.getCurrentZone();
            int lane = e.getCurrentLane();
            switch (currentZone)
            {
                case ZONES.FARTHEST:
                    StartIdleTimer();
                    if (showClusters)
                    {
                        StartFadeOutTimer();
                        showClusters = false;
                    }
                    break;
                case ZONES.FAR:
                    if (showClusters)
                    {
                        StartFadeOutTimer();
                        showClusters = false;
                    }
                    //StopIdleTimer();
                    writingPane.Invalidate();
                    break;
                case ZONES.CLOSE:
                    if (!showClusters)
                    {
                        showClusters = true;
                        StartFadeInTimer();
#if EXPLICIT
                        RestoreNoTool();
#else
                        if (!myInkCollector.CollectingInk)
                        {
                            RestoreNoTool();
                        }
#endif
                    }
                    StopIdleTimer();
                    if ((ambient.GetCurrentAmbientMode() != AMBIENTMODE.ACTIVE)
                        & (ambient.GetCurrentAmbientMode() != AMBIENTMODE.HIDING)) StartHideAmbientTimer();
                    writingPane.Invalidate();
                    break;

                case ZONES.CLOSEST:
                    // Console.WriteLine ("Invoking brighten delegate from ranger event");
                    //BrightenDelegate d = new BrightenDelegate(transPanel1.Brighten);
                    //transPanel1.Invoke(d);
                    if (!this.brighten)
                    {
                        this.brighten = true;
                        writingPane.Invalidate();
                    }
                    if (activeTool == Tool.ERASER) RestoreEraser();
                    else if (activeTool != Tool.NONE) RestorePen();
                    if (showClusters)
                    {
                        StartFadeOutTimer();
                        showClusters = false;
                    }
                    StopIdleTimer();
                    if (NETWORK_PRESENT)
                    {
                        if ((ambient.GetCurrentAmbientMode() != AMBIENTMODE.ACTIVE)
                            & (ambient.GetCurrentAmbientMode() != AMBIENTMODE.HIDING)) StartHideAmbientTimer();
                        writingPane.Invalidate();
                    }
                    break;
            }

        }
        public void audioEvent(AudioEvent e)
        {
            
            mostRecentAudioEvent = e;
            //System.Console.WriteLine("audio event: level is: " + e.getLevel() + " " + System.DateTime.Now.Ticks);
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.SENSOR_UPDATE, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

        }
        public void cameraEvent(CameraEvent e)
        {
            mostRecentCameraEvent = e;
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.CAMERA_EVENT, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

        }

        public void sawFaces(CameraEvent e)
        {

            if (mostRecentCameraEvent != null)
            {
                TimeSpan elapsedTime = System.DateTime.Now - mostRecentCameraEvent.getTimeStamp();
                if (elapsedTime.TotalSeconds < 1 && brighten == false)
                {
                    brighten = true;
                    writingPane.Invalidate();
                }
            }
            mostRecentCameraEvent = e;
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.CAMERA_EVENT, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

            //Console.WriteLine(e.getRawData());
        }

        public void sawFace(CameraEvent e)
        {
            mostRecentCameraEvent = e;
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.CAMERA_EVENT, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

        }

        public void sawBlob(CameraEvent e)
        {
            mostRecentCameraEvent = e;
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.CAMERA_EVENT, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

        }
        public void sawBlobs(CameraEvent e)
        {
            mostRecentCameraEvent = e;
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.CAMERA_EVENT, GetCurrentZone(), GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

        }
        public void distanceEvent(RangerEvent e)
        {
            mostRecentRangerEvent = e;

            if (DEBUG_OUTPUT)
            {
                ZONES zone = e.getCurrentZone();
                int lane = e.getCurrentLane();

                float[] rawData = e.getRawValues();
                Console.Write("entered zone: " + zone);
                Console.Write(" [");
                Console.Write(rawData[0]);
                Console.Write(",");
                Console.Write(rawData[1]);
                Console.Write(",");
                Console.Write(rawData[2]);
                Console.Write(",");
                Console.Write(rawData[3]);
                Console.Write("]");
                Console.Write("\n");
                Console.Write("Lane " + lane + "\n");

            }

        }

        private void InitializeSmartBoard()
        {
            SbsdkEvents = (_ISBSDKBaseClass2Events_Event)Sbsdk;
            //SbsdkEvents.OnCircle += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnCircleEventHandler(this.OnCircle);
            //SbsdkEvents.OnClear += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnClearEventHandler(this.OnClear);
            SbsdkEvents.OnEraser += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnEraserEventHandler(this.OnEraser);
            //SbsdkEvents.OnLine += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnLineEventHandler(this.OnLine);
            //SbsdkEvents.OnNext += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnNextEventHandler(this.OnNext);
            SbsdkEvents.OnNoTool += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnNoToolEventHandler(this.OnNoTool);
            SbsdkEvents.OnPen += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnPenEventHandler(this.OnPen);
            //SbsdkEvents.OnPrevious += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnPreviousEventHandler(this.OnPrevious);
            //SbsdkEvents.OnPrint += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnPrintEventHandler(this.OnPrint);
            //SbsdkEvents.OnRectangle += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnRectangleEventHandler(this.OnRectangle);
            SbsdkEvents.OnBoardStatusChange += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnBoardStatusChangeEventHandler(this.OnSBSDKBoardStatusChange);
            //SbsdkEvents.OnXMLAnnotation += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXMLAnnotationEventHandler(this.OnXMLAnnotation);
            //SbsdkEvents.OnXYNonProjectedDown += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYNonProjectedDownEventHandler(this.OnXYNonProjectedDown);
            //SbsdkEvents.OnXYNonProjectedMove += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYNonProjectedMoveEventHandler(this.OnXYNonProjectedMove);
            //SbsdkEvents.OnXYNonProjectedUp += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYNonProjectedUpEventHandler(this.OnXYNonProjectedUp);
            //SbsdkEvents.OnXYDown += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYDownEventHandler(this.OnXYDown);
            //SbsdkEvents.OnXYMove += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYMoveEventHandler(this.OnXYMove);
            //SbsdkEvents.OnXYUp += new SBSDKComWrapperLib._ISBSDKBaseClass2Events_OnXYUpEventHandler(this.OnXYUp);


            Sbsdk.SBSDKAttach(Handle.ToInt32(), false);

            // always get events through the sdk
            // by default mouse events are sent when no tool is selected
            //Sbsdk.SBSDKSetSendMouseEvents(Handle.ToInt32(), SBCME_NEVER, -1);
        }

        // --------------- Form Events ---------------

        private void WhiteboardApp_Close(object sender, System.EventArgs e)
        {
            Environment.Exit(0);
        }

        /// <summary>
        /// Event Handler from Form Load Event
        /// Setup the ink collector for collection
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void WhiteboardApp_Load(object sender, System.EventArgs e)
        {
    
            
            // show or hide the explicit UI
            ShowExplicitUI(showexplicitUI);

            //show the clock
            
            // Initialize the selection data
            lassoPoints = new ArrayList();
            selectionHandles = new PictureBox[] { leftTopHandle, centerTopHandle, rightTopHandle, leftCenterHandle, rightCenterHandle, leftBottomHandle, centerBottomHandle, rightBottomHandle };
            selectionRect = new Rectangle();
            selectedStrokes = null;
            ambientStrokes = null;

            // Initialize data logging class:
            //UsageLogger.GetInstance().Connect("whiteboard.csv");
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.START_APP, ZONES.CLOSE, PEN_STATUS.IN_TRAY, GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

            // Create the pens used to draw the lasso selection
            connectorPen = new Pen(connectorColor);     // Pen used to draw dotted lasso connector line
            connectorPen.DashStyle = DashStyle.Dash;
            dotEdgePen = new Pen(dotEdgeColor);         // Pen used to draw the outer edge of the lasso dot
            dotPen = new Pen(dotColor);                 // Pen used to draw the center of the lasso dot

            // Create a new ink collector and assign it to this form's window
            //myInkCollector = new InkCollector(this.Handle);
            myInkCollector = new InkCollector(writingPane.Handle);
            myInkCollector.Stroke += new InkCollectorStrokeEventHandler(OnStroke);
            myInkCollector.NewPackets += new InkCollectorNewPacketsEventHandler(myInkCollector_NewPackets);

            // Set the ink collector's pen width
            myInkCollector.DefaultDrawingAttributes.Width = MediumInkWidth;

            // Turn off AutoRedraw -- we'll take care of it
            myInkCollector.AutoRedraw = false;

            // Turn the ink collector on
            myInkCollector.Enabled = true;

            // Set default ink color to black
            SetColor(Color.Black);

            // Make it pretty
            myInkCollector.DefaultDrawingAttributes.FitToCurve = true;
            myInkCollector.DefaultDrawingAttributes.AntiAliased = true;

            IdleTimer.Interval = 5000;
            IdleTimer.AutoReset = false;
            IdleTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnIdleTick);

            BrightenTimer.Interval = 100;
            BrightenTimer.AutoReset = true;
            BrightenTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnBrightenTick);

            StartBrightenTimer();

            ShowAmbientTimer.Interval = 30;
            ShowAmbientTimer.AutoReset = true;
            ShowAmbientTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnShowAmbientTick);

            HideAmbientTimer.Interval = 30;
            HideAmbientTimer.AutoReset = true;
            HideAmbientTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnHideAmbientTick);

            ClockInTimer.Interval = 30;
            ClockInTimer.AutoReset = true;
            ClockInTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnClockInTick);

            FadeInTimer.Interval = 50;
            FadeInTimer.AutoReset = true;
            FadeInTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnFadeInTick);

            FadeOutTimer.Interval = 50;
            FadeOutTimer.AutoReset = true;
            FadeOutTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnFadeOutTick);



            // initialize board components
            if (SMARTBOARD_PRESENT)
            {
                Sbsdk = new SBSDKBaseClass2();
                InitializeSmartBoard();
            }

            // initialize distance components
            if (RANGER_PRESENT)
            {
                ranger = new Ranger();
                ranger.addRangerListener(this);
            }

            // initialize camera
            if (CAMERA_PRESENT)
            {
                camera = new Camera();
                camera.addCameraListener(this);
            }

            if (NETWORK_PRESENT)
            {
                ambient = new Ambient(this);
            }

            // We monitor the level of the audio from the mic (for logging purposes):
            audioLevel = new AudioLevel();
            audioLevel.addAudioListener(this);

            // initialize clustering
            clusterManager = new ClusterManager(myInkCollector);

            // initialize stroke manager
            strokeManager = new StrokeManager(myInkCollector);

            // initialize tool state
            UpdateToolMode(ToolMode.Ink);

            // initialize app state
            SimulateRangerEvent(ZONES.FARTHEST);

            if (ENABLE_AUTOSAVE)
            {
                // initialize and start autosave timer
                AutoSaveTimer.Interval = 10000;
                AutoSaveTimer.AutoReset = true;
                AutoSaveTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnAutoSaveTick);
                AutoSaveTimer.Start();
            }

            PictureBox pictureBox = new PictureBox();
            testImage = ImageUtil.GetImageFromURL("http://farm1.static.flickr.com/151/424911220_61edf63357_t.jpg");
            pictureBox.Image = testImage;
            pictureBox.Size = new System.Drawing.Size(75, 75);

            //Panel panel = new Panel();
            //panel.BackColor = Color.Red;
            //this.Controls.Add(pictureBox);
            //pictureBox.SendToBack();
        }

        /// <summary>
        /// Event Handler from Form Paint Event
        /// 
        /// When no ink is selected, autoredrawing is turned on and
        /// this method doesn't need to do anything.  However, if 
        /// there is a selection, autoredrawing is turned off and
        /// it is necessary to draw the strokes manually
        /// (in order to display the strokes as selected).
        /// 
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void WritingPane_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {

            // Perform the drawing manually.
            Graphics g = e.Graphics;

            //Rectangle rect = new Rectangle(0, 0, this.Size.Width, this.Size.Height);
            //SolidBrush colorBrush = new SolidBrush(Color.FromArgb(50, Color.DarkGray));
            //e.Graphics.FillRectangle(colorBrush, rect);

            // Dim!
            g.Clear(Color.White);

            float[][] ptsArray ={
                new float[] {1, 0, 0, 0, 0},
                new float[] {0, 1, 0, 0, 0},
                new float[] {0, 0, 1, 0, 0},
                new float[] {0, 0, 0, 0.5f, 0},
                new float[] {0, 0, 0, 0, 1}
            };

            /*
            ColorMatrix cm = new ColorMatrix(ptsArray);
            ImageAttributes imgAttrs = new ImageAttributes();
            imgAttrs.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Default);

            g.DrawImage(testImage, new Rectangle(0, 0, 100, 47), 0, 0, 100, 47, GraphicsUnit.Pixel, imgAttrs);
            */

            // Actual painting code
            DrawStrokes(g);

            // Draw the selection rect
            g.DrawRectangle(connectorPen, selectionRect);

            // if clusters are active, draw them
            if (applicationMode != ToolMode.MoveInk)
            {
                //if (showClusters) 
                DrawClusters(g);
            }
            // If the user is drawing a lasso, repaint the lasso points.
            if (ToolMode.Selection == applicationMode)
            {
                DrawLasso(g, 0);
            }
            if (!brighten)
            {
                Rectangle rect = new Rectangle(0, 0, this.Size.Width, this.Size.Height);
                SolidBrush colorBrush = new SolidBrush(Color.FromArgb(50, Color.DarkGray));
                e.Graphics.FillRectangle(colorBrush, rect);
                colorBrush.Dispose();
            }
        }

        /// <summary>
        /// Event Handler from Form Mouse Down Event
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void WritingPane_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                DateTime tstamp = System.DateTime.Now;
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.SYNC_BEGIN, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
                return;
            }

            // If the mouse button was pressed down within the
            // current selection area, update the application mode
            // to indicate that the user is moving ink and record
            // the starting location of the move.  This value will 
            // be used in MouseMove to determine how much the selected
            // ink should be moved.
            if (applicationMode == ToolMode.Selection && selectionRect.Contains(e.X, e.Y))
            {
                UsageLogger.GetInstance().beginTiming();
                UpdateToolMode(ToolMode.MoveInk);
                startMoveLocation = new Point(e.X, e.Y);
                return;
            }

            // Get strokes nearby
            Strokes strokesHit = StrokesNearMousePointForSelection(e.X, e.Y);

            // Otherwise, if in select mode, test to see if there's a stroke there, and select its cluster
            if (applicationMode == ToolMode.Selection && strokesHit.Count > 0)
            {

                // If there are, select the cluster of the first stroke found and start moving
                if (strokesHit.Count > 0)
                {
                    Console.WriteLine("# strokes: " + strokesHit.Count);
                    for (int i = 0; i < strokesHit.Count; i++)
                    {
                        try
                        {
                            Strokes cluster = clusterManager.GetClusterForStroke(strokesHit[i]);
                            if (cluster != null)
                            {
                                SetSelection(cluster);
                                UpdateToolMode(ToolMode.MoveInk);
                                startMoveLocation = new Point(e.X, e.Y);
                                UsageLogger.GetInstance().beginTiming();
                                return;
                            }
                        }
                        catch (Exception e2)
                        {
                            Console.WriteLine("Still a bug in the cluster selection somewhere...");
                            Console.WriteLine(e2.StackTrace);
                            Console.WriteLine(e2.Message);
                        }
                    }
                }
            }

            // Otherwise, if there is a selection and the mouse
            // is pressed, clear the selection.
            if (HasSelection())
            {
                SetSelection(null);
            }
            if (applicationMode == ToolMode.Ink ||
                applicationMode == ToolMode.StrokeErase ||
                applicationMode == ToolMode.Selection)
            {
                UsageLogger.GetInstance().beginTiming();
            }

        }

        /// <summary>
        /// Event Handler from Form Mouse Move Event
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void WritingPane_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            // If the selected ink is currently being moved...
            if (applicationMode == ToolMode.MoveInk && HasSelection())
            {
                // Compute the ink space distance the mouse has moved and
                // move the selected strokes correspondingly.
                Point moveAmount = new Point(e.X - startMoveLocation.X, e.Y - startMoveLocation.Y);
                //myInkCollector.Renderer.PixelToInkSpace(CreateGraphics(), ref moveAmount);
                myInkCollector.Renderer.PixelToInkSpace(writingPane.CreateGraphics(), ref moveAmount);
                selectedStrokes.Move(moveAmount.X, moveAmount.Y);

                // Record the location of the mouse since the next
                // move should be relative to it.
                startMoveLocation.X = e.X;
                startMoveLocation.Y = e.Y;

                // Update the selection rectangle
                SetSelection(selectedStrokes);
                this.writingPane.Refresh();
            }
            else if ((ToolMode.StrokeErase == applicationMode) && (MouseButtons.None != MouseButtons))
            {
                Point pt = new Point(e.X, e.Y);

                // Convert the specified point from pixel to ink space coordinates
                myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref pt);

                EraseStrokes(pt, null);
            }
            else
            {
                // If the mouse has entered the rectangular selection
                // region, disable ink collection and update the cursor
                // to show that the user can move this region.  It is
                // necessary to disable ink collection since ink should not
                // flow from the pen while the user is moving the selection.
                if (applicationMode == ToolMode.Selection
                    && selectionRect.Contains(e.X, e.Y))
                {
                    if (myInkCollector.Enabled)
                    {
                        myInkCollector.Enabled = false;
                        Cursor = System.Windows.Forms.Cursors.SizeAll;
                    }
                }
                // If we're near a stroke, do the same as above, because you can move clusters 
                // by selecting strokes.
                else if (applicationMode == ToolMode.Selection
                    && StrokesNearMousePointForSelection(e.X, e.Y).Count > 0
                    && (MouseButtons.None == MouseButtons))
                {
                    if ((myInkCollector.Enabled) && !myInkCollector.CollectingInk)
                    //if (myInkCollector.Enabled)
                    {
                        // this is troublesome because you can't disable ink while collecting...
                        // but it shouldn't be collecting!!
                        myInkCollector.Enabled = false;
                        Cursor = System.Windows.Forms.Cursors.SizeAll;
                    }
                }
                // Otherwise, enable ink collection and set the cursor
                // to its default value (if necessary).
                else if (!myInkCollector.Enabled && applicationMode != ToolMode.StrokeErase)
                {
                    myInkCollector.Enabled = true;
                    Cursor = System.Windows.Forms.Cursors.Arrow;
                }
            }
            //this.writingPane.Refresh();
        }

        /// <summary>
        /// Event Handler from Form Mouse Up Event
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void WritingPane_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            Console.WriteLine("Begin of mouse up routine.");
            // If the user was moving selected ink and the
            // mouse is raised, set the application mode back to
            // its original value.
            if (e.Button == MouseButtons.Right) {
                DateTime tstamp = System.DateTime.Now;
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.SYNC_END, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
                return;
            }

            if (applicationMode == ToolMode.MoveInk)
            {
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.MOVE_STROKES, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());

                if (ambientStrokes != null)
                {
                    ambientStrokes.Remove(selectedStrokes);
                }
                // recluster the selected strokes
                clusterManager.ReclusterStrokes(selectedStrokes);

                // the line below is probably NOT the way to save previous state, but OK for now
                if (miSelect.Checked)
                {
                    UpdateToolMode(ToolMode.Selection);
                }
                else
                {
                    UpdateToolMode(ToolMode.Ink);
                }
                // Recalculate bounding boxes based on new positions.
                RecalculateBoundingBoxes();
            }
            else if (applicationMode == ToolMode.Ink)
            {
                DateTime tstamp = System.DateTime.Now;
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.DRAW_STROKE, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
                UsageLogger.captureImage(this, tstamp);
            }
            else if (applicationMode == ToolMode.StrokeErase)
            {
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.ERASE_STROKES, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
            }
            else if (applicationMode == ToolMode.Selection)
            {
                UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.SELECTION, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
            }

            Console.WriteLine("End of mouse up routine.");
            //Invalidate();
            this.writingPane.Invalidate();
        }

        /// <summary>
        /// Event Handler from Ink->Clear Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miClear_Click(object sender, System.EventArgs e)
        {
            // Clear the selection and delete all strokes from the ink
            EraseStrokes(myInkCollector.Ink.Strokes);
            SetSelection(null);

        }

        /// <summary>
        /// Event Handler from Ink->Exit Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miExit_Click(object sender, System.EventArgs e)
        {
            // Although not required, it is good practice to disable
            // ink collection before exiting
            myInkCollector.Enabled = false;

            Application.Exit();
        }

        /// <summary>
        /// Event Handler from Edit Select Event
        /// 
        /// If the Edit menu is selected, check whether the object
        /// that is currently on the clipboard can be pasted into 
        /// the ink.  Disable the Paste item if it can't be pasted.
        /// Also, disable Copy and Cut if no ink is selected.
        /// 
        ///  
        /// Allowable formats:  Ink Serialized Format or Embeddable OLE
        /// Object (Text Ink or Sketch Ink). 
        /// 
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>       
        private void miMainEdit_Popup(object sender, System.EventArgs e)
        {
            miPaste.Enabled = myInkCollector.Ink.CanPaste() || Clipboard.GetDataObject().GetDataPresent(DataFormats.Text);
            miCopy.Enabled = HasSelection();
            miCut.Enabled = HasSelection();
        }

        /// <summary>
        /// Event Handler from Edit->Cut Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miCut_Click(object sender, System.EventArgs e)
        {

            // Use helper method to cut the selection
            CopyInkToClipboard(InkClipboardModes.Cut);

            SetSelection(null);

        }

        /// <summary>
        /// Event Handler from Edit->Copy Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miCopy_Click(object sender, System.EventArgs e)
        {

            // Use helper method to copy the selection
            CopyInkToClipboard(InkClipboardModes.Copy);

        }

        /// <summary>
        /// Event Handler from Edit->Paste Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>   
        private void miPaste_Click(object sender, System.EventArgs e)
        {
            if (myInkCollector.Ink.CanPaste())
            {
                // Compute the location where the ink should be pasted;
                // this location should be shifted from the origin
                // to account for the width of the selection rectangle's handle.
                Point offset = new Point(leftTopHandle.Width + 1, leftTopHandle.Height + 1);
                //myInkCollector.Renderer.PixelToInkSpace(CreateGraphics(), ref offset);
                myInkCollector.Renderer.PixelToInkSpace(writingPane.CreateGraphics(), ref offset);

                // Use Ink API to paste the clipboard data into the Ink
                Strokes pastedStrokes = myInkCollector.Ink.ClipboardPaste(offset);

                // If the contents of the clipboard were a valid format 
                // (Ink Serialized Format or Embeddable OLE Object) and at
                // least one stroke was pasted into the ink, use a helper 
                // method to update the stroke selection.  Otherwise,
                // the result will be null and this paste becomes a no-op.  
                if (null != pastedStrokes)
                {
                    SetSelection(pastedStrokes);
                }

            }
            else if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
            {
                // if it's XML, paste it on the whiteboard
                Ink ink = new Ink();
                try
                {
                    String text = (String)Clipboard.GetDataObject().GetData(DataFormats.Text);
                    XmlTextReader reader = new XmlTextReader(new StringReader(text));
                    minX = minY = maxX = maxY = int.MinValue;
                    while (reader.Read())
                    {
                        string lcname = reader.Name.ToLower();
                        switch (reader.NodeType)
                        {
                            case XmlNodeType.Document:
                                break;
                            case XmlNodeType.Element:
                                if (lcname == "strokes" || lcname == "page")
                                {
                                    if (reader.HasAttributes)
                                    {
                                        for (int i = 0; i < reader.AttributeCount; i++)
                                        {
                                            reader.MoveToAttribute(i);
                                            lcname = reader.Name.ToLower();
                                            // get min and max x and y
                                            if (lcname == "minx")
                                            {
                                                minX = int.Parse(reader.Value);
                                            }
                                            else if (lcname == "miny")
                                            {
                                                minY = int.Parse(reader.Value);
                                            }
                                            else if (lcname == "maxx")
                                            {
                                                maxX = int.Parse(reader.Value);
                                            }
                                            else if (lcname == "maxy")
                                            {
                                                maxY = int.Parse(reader.Value);
                                            }
                                        }
                                    }
                                    break;
                                }
                                if (lcname == "stroke")
                                {
                                    strokesList = new ArrayList();
                                    break;
                                }
                                if (lcname == "p")
                                {
                                    int x = int.MinValue;
                                    int y = int.MinValue;
                                    if (reader.HasAttributes)
                                    {
                                        for (int i = 0; i < reader.AttributeCount; i++)
                                        {
                                            reader.MoveToAttribute(i);
                                            // change floats to ints by multiplying by 1000 and casting
                                            if (string.Compare(reader.Name, "x", true) == 0)
                                            {
                                                float fx = float.Parse(reader.Value);
                                                x = (int)(fx * 1000);
                                            }
                                            else if (string.Compare(reader.Name, "y", true) == 0)
                                            {
                                                float fy = float.Parse(reader.Value);
                                                y = (int)(fy * 1000);
                                            }
                                        }
                                        if (x != int.MinValue && y != int.MinValue)
                                        {
                                            strokesList.Add(new Point(x, y));
                                            x = int.MinValue;
                                            y = int.MinValue;
                                        }
                                    }
                                    break;
                                }
                                break;
                            case XmlNodeType.EndElement:
                                if (lcname == "stroke" && strokesList != null)
                                {
                                    Point[] strokesArray = (Point[])strokesList.ToArray(typeof(Point));
                                    ink.CreateStroke(strokesArray);
                                    strokesList = null;
                                    //TabletPropertyMetrics theMetric = ink.Strokes[0].GetPacketDescriptionPropertyMetrics(PacketProperty.NormalPressure);
                                    //Console.WriteLine(theMetric.Maximum);
                                    //Console.WriteLine(theMetric.Minimum);
                                }
                                break;
                            case XmlNodeType.Text:
                                break;
                            // There are many other types of nodes, but
                            // we are not interested in them
                        }
                    }
                }
                catch (XmlException xe)
                {
                    Console.WriteLine("XML Exception:" + xe.Message);
                }
                // render to this rectangle with given onscreen size, if all values were found
                Point rectPt;
                if (minX != int.MinValue && minX != int.MinValue && minX != int.MinValue && minX != int.MinValue)
                {
                    rectPt = new Point(maxX - minX, maxY - minY);
                }
                else
                {
                    rectPt = new Point(200, 200);
                }
                myInkCollector.Renderer.PixelToInkSpace(writingPane.Handle, ref rectPt);
                Rectangle theRectangle = new Rectangle(0, 0, rectPt.X, rectPt.Y);
                myInkCollector.Ink.AddStrokesAtRectangle(ink.Strokes, theRectangle);
                writingPane.Invalidate();
                return;
            }
            else
            {
                MessageBox.Show("Unable to paste ink.");
            }
        }

        /// <summary>
        /// Event Handler from Edit->Delete Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miDelete_Click(object sender, System.EventArgs e)
        {
            Strokes strokesToDelete = selectedStrokes;
            SetSelection(null);
            EraseStrokes(strokesToDelete);
        }

        private void miSaveAsMenu_Click(object sender, EventArgs e)
        {
            /// Create a stream which will be used to save data to the output file
            Stream myStream = null;

            /// Create the SaveFileDialog, which presents a standard Windows
            /// Save dialog to the user.
            SaveFileDialog saveDialog = new SaveFileDialog();

            /// Set the filter to suggest our recommended extensions
            saveDialog.Filter =
                "Ink Serialized Format files (*.isf)|*.isf|XML files (*.xml)|*.xml|" +
                "HTML files (*.htm)|*.htm";

            /// If the dialog exits and the user didn't choose Cancel
            if (saveDialog.ShowDialog() == DialogResult.OK)
            //if (true)
            {
                try
                {
                    /// Attempt to Open the file with read/write permission
                    myStream = saveDialog.OpenFile();
                    //myStream = System.IO.File.Open("myisf.isf", FileMode.Create, FileAccess.ReadWrite);
                    if (myStream != null)
                    {
                        // Put the filename in a more canonical format
                        String filename = saveDialog.FileName.ToLower();

                        // Get a version of the filename without an extension
                        // This will be used for saving the associated image
                        String extensionlessFilename = Path.GetFileNameWithoutExtension(filename);

                        // Get the extension of the file 
                        String extension = Path.GetExtension(filename);

                        // Use the extension to determine what form to save the data in
                        switch (extension)
                        {
                            case ".xml":
                                StrokeUtil.SaveXML(myInkCollector, myStream);
                                break;

                            /// The two HTML cases require a filename for saving associated images
                            case ".htm":
                            case ".html":
                                StrokeUtil.SaveHTML(myInkCollector, myStream, extensionlessFilename);
                                break;

                            /// If unfamiliar with the extension, use ISF, the most "native format"
                            case ".isf":
                            default:
                                StrokeUtil.SaveISF(myInkCollector, myStream);
                                break;
                        }
                    }
                    else
                    {
                        // Throw an exception if a null pointer is returned for the stream
                        throw new IOException();
                    }
                }
                catch (IOException /*ioe*/)
                {
                    MessageBox.Show("File error");
                }
                finally
                {
                    // Close the stream in the finally clause so it
                    // is always reached, regardless of whether an 
                    // exception occurs.  SaveXML, SaveHTML, and
                    // SaveISF can throw, so this precaution is necessary.
                    if (null != myStream)
                    {
                        myStream.Close();
                    }
                }
            } // End if user chose OK from dialog 
        }

        private void miSelectAllMenu_Click(object sender, EventArgs e)
        {
            SetSelection(myInkCollector.Ink.Strokes);
        }

        /// <summary>
        /// Event Handler from Edit->Select Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miSelect_Click(object sender, System.EventArgs e)
        {
            // Set the application mode to lasso selection
            UpdateToolMode(ToolMode.Selection);
        }


        /// <summary>
        /// Event Handler from Edit->Ink Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miInk_Click(object sender, System.EventArgs e)
        {
            // Set the application mode to ink collection
            UpdateToolMode(ToolMode.Ink);
        }

        /// <summary>
        /// Event Handler from Format->ISF Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miISF_Click(object sender, System.EventArgs e)
        {
            miISF.Checked = !miISF.Checked;
        }

        /// <summary>
        /// Event Handler from Metafile->ISF Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miMetafile_Click(object sender, System.EventArgs e)
        {
            miMetafile.Checked = !miMetafile.Checked;
        }

        /// <summary>
        /// Event Handler from Format->EMF Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miEMF_Click(object sender, System.EventArgs e)
        {
            miEMF.Checked = !miEMF.Checked;
        }

        /// <summary>
        /// Event Handler from Format->BMP Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miBMP_Click(object sender, System.EventArgs e)
        {
            miBMP.Checked = !miBMP.Checked;
        }

        /// <summary>
        /// Event Handler from Format->Text Ink Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miTInk_Click(object sender, System.EventArgs e)
        {
            miSInk.Checked = false;
            miTInk.Checked = true;
            miNoOLE.Checked = false;
        }

        /// <summary>
        /// Event Handler from Format->Sketch Ink Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miSInk_Click(object sender, System.EventArgs e)
        {
            miSInk.Checked = true;
            miTInk.Checked = false;
            miNoOLE.Checked = false;
        }

        /// <summary>
        /// Event Handler from Format->No OLE Object Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>  
        private void miNoOLE_Click(object sender, System.EventArgs e)
        {
            miSInk.Checked = false;
            miTInk.Checked = false;
            miNoOLE.Checked = true;
        }

        /// <summary>
        /// Event Handler from Style->Color->Red Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miRed_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected color.
            SetColor(Color.Red);

        }

        /// <summary>
        /// Event Handler from Style->Color->Green Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miGreen_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected color.
            SetColor(Color.LightGreen);

        }

        /// <summary>
        /// Event Handler from Style->Color->Blue Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miBlue_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected color.
            SetColor(Color.Blue);

        }

        /// <summary>
        /// Event Handler from Style->Color->Black Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miWhite_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected color.
            SetColor(Color.White);

        }

        /// <summary>
        /// Event Handler from Style->Width->Thin Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miThin_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected pen width.
            SetWidth(ThinInkWidth);

        }

        /// <summary>
        /// Event Handler from Style->Width->Medium Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miMedium_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected pen width.
            SetWidth(MediumInkWidth);

        }

        /// <summary>
        /// Event Handler from Style->Width->Thick Menu Item
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void miThick_Click(object sender, System.EventArgs e)
        {

            // Use helper method to set the InkCollector and the
            // selected strokes to the selected pen width.
            SetWidth(ThickInkWidth);

        }

        // --------------- Ink Collector Events ---------------

        /// <summary>
        /// Event Handler from Ink Collector's NewPackets event
        /// 
        /// This event is fired when the Ink Collector receives 
        /// new packet data and the user is drawing a lasso.  
        /// In this case,  it is necessary to intercept the new packets
        /// and use them to draw the selection lasso.
        /// </summary>
        /// <param name="sender">The control that raised the event.</param>
        /// <param name="e">The event arguments.</param>
        private void myInkCollector_NewPackets(object sender, InkCollectorNewPacketsEventArgs e)
        {
            if (applicationMode != ToolMode.Selection)
            {
                return;
            }

            // retrieve the size of each packet
            int packetSize = e.Stroke.PacketSize;

            // the index where the new lasso points will be added
            int newPacketIndex = lassoPoints.Count;

            // Add each new packet into the lasso points collection
            for (int i = 0; i < e.PacketCount; i++)
            {

                // retrieve the x and y packet values 
                Point pt = new Point(e.PacketData[i * packetSize + XPacketIndex],
                    e.PacketData[i * packetSize + YPacketIndex]);
                //myInkCollector.Renderer.InkSpaceToPixel(this.Handle, ref pt);
                myInkCollector.Renderer.InkSpaceToPixel(writingPane.Handle, ref pt);


                // If the x or y packet values fall outside of
                // the drawing area , update them to the nearest
                // location on the drawing area.  This adjustment
                // prevents the lasso's boundaries from going 
                // beyond the drawing area.
                if (pt.X < 0)
                {
                    pt.X = 0;
                }
                if (pt.X > ClientSize.Width)
                {
                    pt.X = ClientSize.Width;
                }
                if (pt.Y < 0)
                {
                    pt.Y = 0;
                }
                if (pt.Y > this.ClientSize.Height)
                {
                    pt.Y = ClientSize.Height;
                }

                // Add the new point into the lasso points collection
                lassoPoints.Add(pt);

            }

            // Draw the updated lasso.  In order to improve performance,
            // only the new lasso packets will be drawn.
            //DrawLasso(CreateGraphics(), newPacketIndex);
            DrawLasso(writingPane.CreateGraphics(), newPacketIndex);
        }

        // --------------- Helper Methods ---------------

        /// <summary>
        /// Helper method to convert a rectangle in pixel coordinates
        /// to ink space coordinates.  (1 ink space unit = .01mm)
        /// 
        /// Note that this method works since there is guaranteed not
        /// to be any rotation in this sample.
        /// </summary>
        /// <param name="g">The graphics display</param>
        /// <param name="r">The rectangle to convert</param>
        private void PixelToInkSpace(Graphics g, ref Rectangle r)
        {

            Point leftTop = new Point(r.X, r.Y);
            Point widthHeight = new Point(r.Width, r.Height);
            myInkCollector.Renderer.PixelToInkSpace(g, ref leftTop);
            myInkCollector.Renderer.PixelToInkSpace(g, ref widthHeight);

            r.Location = leftTop;
            r.Size = new Size(widthHeight);

        }

        /// <summary>
        /// Helper method to convert a rectangle in ink space
        /// coordinates to pixel coordinates.  (1 ink space unit = .01mm)
        /// 
        /// Note that this method works since there is guaranteed not
        /// to be any rotation in this sample.
        /// </summary>
        /// <param name="g">The graphics display</param>
        /// <param name="r">The rectangle to convert</param>
        private void InkSpaceToPixel(Graphics g, ref Rectangle r)
        {
            Point leftTop = new Point(r.X, r.Y);
            Point widthHeight = new Point(r.Width, r.Height);
            myInkCollector.Renderer.InkSpaceToPixel(g, ref leftTop);
            myInkCollector.Renderer.InkSpaceToPixel(g, ref widthHeight);

            r.Location = leftTop;
            r.Size = new Size(widthHeight);

        }

        /// <summary>
        /// This method uses the Ink API to copy ink onto
        /// the clipboard.
        /// </summary>
        /// <param name="clipboardModes">The clipboard modes (such as copy or paste)</param>
        private void CopyInkToClipboard(InkClipboardModes clipboardModes)
        {

            // Declare the ink clipboard formats to put on the clipboard
            InkClipboardFormats formats = new InkClipboardFormats();

            // Use selected format menu items to set the clipboard formats
            if (miISF.Checked)
            {
                formats |= InkClipboardFormats.InkSerializedFormat;
            }
            if (miMetafile.Checked)
            {
                formats |= InkClipboardFormats.Metafile;
            }
            if (miEMF.Checked)
            {
                formats |= InkClipboardFormats.EnhancedMetafile;
            }
            if (miBMP.Checked)
            {
                formats |= InkClipboardFormats.Bitmap;
            }
            if (miSInk.Checked)
            {
                formats |= InkClipboardFormats.SketchInk;
            }
            if (miTInk.Checked)
            {
                formats |= InkClipboardFormats.TextInk;
            }

            // If at least one format was selected, invoke the Ink
            // API's ClipboardCopy method.  Note that selectedStrokes
            // could be null, but that this is ok - if selectedStrokes
            // is null, all of the ink is copied.
            if (formats != InkClipboardFormats.None)
            {
                myInkCollector.Ink.ClipboardCopy(selectedStrokes, formats, clipboardModes);
            }
            else
            {
                MessageBox.Show("No clipboard formats selected");
            }
        }

        /// <summary>
        /// Sets the selected strokes and selection rectangle.
        /// </summary>
        /// <param name="strokes">The strokes that should be selected.  If null, the selection becomes empty</param>
        private void SetSelection(Strokes strokes)
        {
            // Tracks whether the rectangle that bounds the selected
            // strokes should be displayed
            bool isSelectionVisible = false;

            // Update the selected strokes collection
            selectedStrokes = strokes;

            // If no strokes are selected, set the selection rectangle
            // to empty
            if (!HasSelection())
            {
                selectionRect = Rectangle.Empty;
            }
            // Otherwise, at least one stroke is selected and it is necessary
            // to display the selection rectangle.
            else
            {
                isSelectionVisible = true;

                // Retrieve the bounding box of the strokes
                selectionRect = selectedStrokes.GetBoundingBox();
                InkSpaceToPixel(writingPane.CreateGraphics(), ref selectionRect);

                // Pad the selection rectangle so that the selected ink 
                // doesn't overlap with the selection rectangle's handles.
                selectionRect.Inflate(SelectionRectBuffer, SelectionRectBuffer);

                // compute the center of the rectangle that bounds the 
                // selected strokes
                int xAvg = (selectionRect.Right + selectionRect.Left) / 2;
                int yAvg = (selectionRect.Top + selectionRect.Bottom) / 2;

                // Draw the resize handles
                // top left
                SetLocation(selectionHandles[0], selectionRect.Left, selectionRect.Top);
                // top
                SetLocation(selectionHandles[1], xAvg, selectionRect.Top);
                // top right 
                SetLocation(selectionHandles[2], selectionRect.Right, selectionRect.Top);

                // left 
                SetLocation(selectionHandles[3], selectionRect.Left, yAvg);
                // right
                SetLocation(selectionHandles[4], selectionRect.Right, yAvg);

                // bottom left
                SetLocation(selectionHandles[5], selectionRect.Left, selectionRect.Bottom);
                // bottom
                SetLocation(selectionHandles[6], xAvg, selectionRect.Bottom);
                // bottom right
                SetLocation(selectionHandles[7], selectionRect.Right, selectionRect.Bottom);
            }

            // Set the visibility of each selection handle in the 
            // selection rectangle.  If there is no selection, all 
            // handles should be hidden.  Otherwise, all handles should
            // be visible.
            foreach (PictureBox pb in selectionHandles)
            {
                pb.Visible = isSelectionVisible;
            }

            // Since the selection has changed, repaint the screen.
            Refresh();
        }

        /// <summary>
        /// Helper method to set the location of the selection rectangle
        /// handle.
        /// </summary>
        /// <param name="pb">The handle's picture box</param>
        /// <param name="xCenter">The desired x coordinate of the center</param>
        /// <param name="yCenter">The desired y coordinate of the center</param>
        public void SetLocation(PictureBox pb, int xCenter, int yCenter)
        {
            Point location = new Point();

            location.X = xCenter - pb.Width / 2;
            location.Y = yCenter - pb.Height / 2;

            pb.Location = location;
        }

        /// <summary>
        /// Helper method to determine if there is a selection
        /// </summary>
        /// <returns>True if there is a selection</returns>
        public bool HasSelection()
        {
            return selectedStrokes != null && selectedStrokes.Count > 0;
        }

        /// <summary>
        /// Draws the selection lasso.
        /// 
        /// Note that this method does NOT draw a point for every piece
        /// of packet data.  Instead, it draws evenly spaced dots that
        /// fall along the lasso path.
        /// </summary>
        /// <param name="g">The graphics device</param>
        /// <param name="newPacketIndex">The index where the new packets begin</param>
        public void DrawLasso(Graphics g, int newPacketIndex)
        {

            // The distance between the current point and the next point
            int dx, dy;

            // The distance and angle of the line connecting the current 
            // point and the next point
            double lineLength;
            double angle;

            // The horizontal and vertical spacing between the dots
            double segSpaceX, segSpaceY;

            // The total number of dots to draw
            int totalSegments;

            // Undraw the line that connected the first point to the 
            // last point before the latest packets were added.
            DrawLassoConnector(0, newPacketIndex - 1);

            // For each lasso point, draw the corresponding lasso dots.
            // Note that this method does NOT draw a point for every piece
            // of packet data.  Instead, it draws evenly spaced dots that
            // fall along the lasso path.
            for (int i = newPacketIndex; i < lassoPoints.Count; i++)
            {
                //  Always draw the first packet...
                if (0 == i)
                {
                    // Update the last drawn lasso dot to this packet
                    lastDrawnLassoDot.X = ((Point)lassoPoints[i]).X;
                    lastDrawnLassoDot.Y = ((Point)lassoPoints[i]).Y;

                    // use a helper method to draw the lasso dot
                    DrawSelectionInkDot(g, lastDrawnLassoDot.X, lastDrawnLassoDot.Y);
                }
                else
                {
                    // Compute the x and y distance between this point and the last drawn
                    // lasso dot
                    dx = ((Point)lassoPoints[i]).X - lastDrawnLassoDot.X;
                    dy = ((Point)lassoPoints[i]).Y - lastDrawnLassoDot.Y;

                    // Compute length of the line between this point and the last drawn
                    // lasso dot
                    lineLength = Math.Sqrt(dx * dx + dy * dy);

                    // If the spacing between this packet and the last drawn lasso
                    // dot is greater than the desired DotSpacing, draw as many
                    // evently spaced lasso points as needed to fill the distance...
                    if (DotSpacing <= lineLength)
                    {

                        // Calculate the angle of the line connecting this point 
                        // to the last drawn lasso point. 
                        angle = Math.Atan2(dy, dx);

                        // Calculate the x and y components of the dot spacing
                        segSpaceX = DotSpacing * Math.Cos(angle);
                        segSpaceY = DotSpacing * Math.Sin(angle);

                        // Calculate the number of dots we will need to draw.
                        totalSegments = (int)(lineLength / (double)DotSpacing);

                        // draw the lasso dots...
                        for (int j = 0; j < totalSegments; j++)
                        {
                            // The coordinates of the lasso dot are determined by the
                            // previous lasso dot plus the x and y components of the
                            // dot spacing (computed above).
                            lastDrawnLassoDot.X = (int)(lastDrawnLassoDot.X + segSpaceX);
                            lastDrawnLassoDot.Y = (int)(lastDrawnLassoDot.Y + segSpaceY);

                            // use a helper method to draw the lasso dot
                            DrawSelectionInkDot(g, lastDrawnLassoDot.X, lastDrawnLassoDot.Y);

                        }
                    }
                }
            }

            // draw the line connecting the first point to the last point
            DrawLassoConnector(0, lassoPoints.Count - 1);
        }

        /// <summary>
        /// Helper method to draw/undraw the lasso connector line
        /// </summary>
        /// <param name="start">The index of the start point</param>
        /// <param name="end">The index of the end point</param>
        /// <param name="points">The lasso points</param>
        private void DrawLassoConnector(int start, int end)
        {
            if ((end > start) && (lassoPoints.Count > 1) && (end < lassoPoints.Count))
            {
                // Draw a reversible line from the point at start
                // to the point at end.
                Point startPoint = (Point)lassoPoints[start];
                Point endPoint = (Point)lassoPoints[end];

                // Convert to screen space
                startPoint = writingPane.PointToScreen(startPoint);
                endPoint = writingPane.PointToScreen(endPoint);

                // Reverse the line - this is the .NET way of drawing
                // an XOR line.
                ControlPaint.DrawReversibleLine(startPoint, endPoint, BackColor);
            }
        }

        /// <summary>
        /// Helper method to draw a lasso seletion dot
        /// </summary>
        /// <param name="g">The graphics device</param>
        /// <param name="x">The x coordinate of the center of the dot</param>
        /// <param name="y">The y coordinate of the center of the dot</param>
        private void DrawSelectionInkDot(Graphics g, int x, int y)
        {
            //
            // Offset the x and y by 1/2 the dot size (biasing lower-right)
            //
            x = x - (DotSize / 2);
            y = y - (DotSize / 2);

            // Draw the inside of the lasso dot
            g.FillEllipse(dotPen.Brush, x, y, DotSize, DotSize);

            // Draw the outside edge of the lasso dot
            g.DrawEllipse(dotEdgePen, x, y, DotSize, DotSize);

        }

        /// <summary>
        /// This method is used to render ink when there is
        /// a selection.
        /// </summary>
        /// <param name="g">The graphics device</param>
        private void DrawStrokes(Graphics g)
        {

            // First, draw the strokes that are not part of the 
            // selection.  These strokes will appear the same
            // as the InkCollector would normally draw them.
            Strokes unselectedStrokes = myInkCollector.Ink.Strokes;

            // If there are no selected strokes, draw them all and go away
            if (selectedStrokes == null)
            {
                myInkCollector.Renderer.Draw(g, unselectedStrokes);
                return;
            }

            // Otherwise, draw the unselected ones first
            unselectedStrokes.Remove(selectedStrokes);
            myInkCollector.Renderer.Draw(g, unselectedStrokes);

            // Next, draw each selected stroke.  A selected stroke
            // is rendered in white with a selection border.
            foreach (Stroke selectedStroke in selectedStrokes)
            {
                DrawingAttributes da = selectedStroke.DrawingAttributes.Clone();
                da.Color = SystemColors.WindowText;
                da.Width = da.Width + SelectedInkWidthIncrease;
                myInkCollector.Renderer.Draw(g, selectedStroke, da);
                da.Color = Color.White;
                da.Width = da.Width - SelectedInkWidthIncrease;
                myInkCollector.Renderer.Draw(g, selectedStroke, da);
            }
        }

        /// <summary>
        /// Helper method to set the InkCollector and the
        /// selected stroke color.
        /// </summary>
        /// <param name="newColor">The new color</param>
        private void SetColor(Color newColor)
        {
            myInkCollector.DefaultDrawingAttributes.Color = newColor;

            // In addition to updating the ink collector, also update
            // the drawing attributes of all selected strokes.
            if (HasSelection())
            {
                foreach (Stroke s in selectedStrokes)
                {
                    s.DrawingAttributes.Color = newColor;
                }
            }

            Refresh();
        }

        /// <summary>
        /// Helper method to set the InkCollector and the
        /// selected stroke pen width.
        /// </summary>
        /// <param name="newWidth">The new pen width</param>
        private void SetWidth(float newWidth)
        {
            myInkCollector.DefaultDrawingAttributes.Width = newWidth;

            // In addition to updating the ink collector, also update
            // the drawing attributes of all selected strokes.
            if (HasSelection())
            {
                foreach (Stroke s in selectedStrokes)
                {
                    s.DrawingAttributes.Width = newWidth;
                }
            }

            Refresh();
        }

        // This function handles the Open... command. It will determine the type of ink which is being
        // opened and call the appropriate helper routine.
        // The try...catch section in this function will handle all of the error
        // handling for the functions it calls.
        private void miOpenMenu_Click(object sender, System.EventArgs e)
        {
            /// Create a stream which will be used to load data from the output file
            Stream myStream = null;

            /// Create the OpenFileDialog, which presents a standard Windows
            /// dialog to the user.
            OpenFileDialog openDialog = new OpenFileDialog();

            /// Set the filter to suggest our recommended extensions
            openDialog.Filter =
                "All Known Files (*.isf; *.xml)|*.isf; *.xml|Ink Serialized Format files (*.isf)|*.isf|" +
                "XML files (*.xml)|*.xml|All Files (*.*)|*.*";

            /// If the dialog exits and the user didn't choose Cancel
            if (openDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    /// Attempt to Open the file with read only permission
                    myStream = openDialog.OpenFile();
                    if (myStream != null)
                    {
                        // Put the filename in a more canonical format
                        String filename = openDialog.FileName.ToLower();

                        // Get the extension of the file 
                        String extension = filename.Substring(filename.LastIndexOf('.'));

                        // Use the extension to determine what form to save the data in
                        switch (extension)
                        {
                            case ".isf":
                                StrokeUtil.LoadISF(myInkCollector, myStream);
                                break;

                            case ".xml":
                                StrokeUtil.LoadXML(myInkCollector, myStream);
                                break;

                            // If unfamiliar with the extension, assume ISF, the most "native format"
                            default:
                                StrokeUtil.LoadISF(myInkCollector, myStream);
                                break;
                        }
                        writingPane.Invalidate();
                    }
                    else
                    {
                        // Throw an exception if a null pointer is returned for the stream
                        throw new IOException();
                    }
                }
                catch (IOException /*ioe*/)
                {
                    MessageBox.Show("File error");
                }
                catch
                {
                    // If the xml or the scanned form image file are not available,
                    // display an error and exit
                    MessageBox.Show("An error occured while loading ink from the specified file.\n" +
                        "Please verify that the file contains valid serialized ink and try again.",
                        "Serialization",
                        MessageBoxButtons.OK);
                }
                finally
                {
                    // Close the stream in the finally clause so it
                    // is always reached, regardless of whether an 
                    // exception occurs.  LoadXML, LoadHTML, and
                    // LoadISF can throw, so this precaution is necessary.
                    if (null != myStream)
                    {
                        myStream.Close();
                    }
                }
            }
        }

        public void LoadAnotoXML(Stream s, int imageIndex)
        {
            // Load a particular Anoto notebook page as XML from the given stream,
            // and place it onto the screen in one of two slots (given by imageIndex = 0 or 1).
            //
            // We also keep track of the strokes that are associated with the notebook
            // pages, so that we can treat them differently from regular strokes.

            Strokes newStrokes = StrokeUtil.GetStrokesFromAnotoXML(myInkCollector, writingPane, clusterManager, s, imageIndex);
            if (imageIndex == 0)
            {
                ambientStrokes = newStrokes;
            }
            else
            {
                ambientStrokes.Add(newStrokes);
            }
            RecalculateBoundingBoxes();
        }

        public ZONES GetCurrentZone()
        {
            return currentZone;
        }

        // tool events
        private void OnPen(int iPointerID)
        {
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.PEN_GRABBED, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
            myInkCollector.DefaultDrawingAttributes.Width = MediumInkWidth;
            if (SMARTBOARD_PRESENT)
            {
                int iRed, iGreen, iBlue;
                Sbsdk.SBSDKGetToolColor(iPointerID, out iRed, out iGreen, out iBlue);
                Console.WriteLine("Color: " + iRed + "," + iGreen + "," + iBlue);
                if (iRed > 0)
                {
                    myInkCollector.DefaultDrawingAttributes.Color = Color.Red;
                    activeTool = Tool.RED_PEN;
                }
                else if (iGreen > 0)
                {
                    myInkCollector.DefaultDrawingAttributes.Color = Color.LightGreen;
                    activeTool = Tool.GREEN_PEN;
                }
                else if (iBlue > 0)
                {
                    myInkCollector.DefaultDrawingAttributes.Color = Color.Blue;
                    activeTool = Tool.BLUE_PEN;
                }
                else
                {
                    myInkCollector.DefaultDrawingAttributes.Color = Color.Black;
                    activeTool = Tool.BLACK_PEN;
                }
            }
            else
            {
                myInkCollector.DefaultDrawingAttributes.Color = Color.Black;
                activeTool = Tool.BLACK_PEN;
            }
            ModeChangeDelegate d = new ModeChangeDelegate(UpdateToolMode);
#if EXPLICIT        
            if (lastButton != ButtonMode.MoveSelect)
            {
                Invoke(d, new Object[] { ApplicationMode.Ink });
            }
#else
            Invoke(d, new Object[] { ToolMode.Ink });
#endif

            ClearAmbientStrokes();
            this.writingPane.Invalidate();
        }

        public void ClearAmbientStrokes()
        {
            if (ambientStrokes != null)
            {
                EraseStrokes(ambientStrokes);
                //foreach (Stroke st in ambientStrokes)
                //{
                //    clusterManager.RemoveFromCluster(st);
                //    myInkCollector.Ink.DeleteStroke(st);
                //}

            }
            ambientStrokes = null;
            RecalculateBoundingBoxes();
        }


        private void RestorePen()
        {

            myInkCollector.DefaultDrawingAttributes.Width = MediumInkWidth;
            if (SMARTBOARD_PRESENT)
            {
                switch (activeTool)
                {
                    case Tool.RED_PEN:
                        myInkCollector.DefaultDrawingAttributes.Color = Color.Red;
                        break;
                    case Tool.GREEN_PEN:
                        myInkCollector.DefaultDrawingAttributes.Color = Color.LightGreen;
                        break;
                    case Tool.BLUE_PEN:
                        myInkCollector.DefaultDrawingAttributes.Color = Color.Blue;
                        break;
                    default:

                        myInkCollector.DefaultDrawingAttributes.Color = Color.Black;
                        activeTool = Tool.BLACK_PEN;
                        break;
                }
            }
            else
            {
                myInkCollector.DefaultDrawingAttributes.Color = Color.Black;
                activeTool = Tool.BLACK_PEN;
            }
            ModeChangeDelegate d = new ModeChangeDelegate(UpdateToolMode);
#if EXPLICIT
            if (lastButton != ButtonMode.MoveSelect){
                Invoke(d, new Object[] { ApplicationMode.Ink });
            }
#else
            Invoke(d, new Object[] { ToolMode.Ink });
#endif

        }

        private void OnNoTool(int iPointerID)
        {
            UsageLogger.logEvent(System.DateTime.Now, EVENT_TYPE.PEN_RELEASED, currentZone, GetPenStatus(), GetMostRecentRangerEvent(), GetMostRecentAudioEvent(), GetMostRecentCameraEvent());
            OnNoTool();
        }

        private void OnNoTool()
        {
            Console.Write("no tool");
            activeTool = Tool.NONE;
            RestoreNoTool();
        }

        private void RestoreNoTool()
        {
            Console.WriteLine("SETTING NO TOOL MODE");
            // Set the tool mode to None.
            //activeTool = Tool.NONE;
            ModeChangeDelegate d = new ModeChangeDelegate(UpdateToolMode);
#if EXPLICIT
            if (lastButton != ButtonMode.Write)
            {
                Invoke(d, new Object[] { ApplicationMode.Selection });
            }
#else
            Invoke(d, new Object[] { ToolMode.Selection });
#endif

        }
        private void OnEraser(int iPointerID)
        {
            // Set the tool mode to Eraser.
            //add condition based on mode
                activeTool = Tool.ERASER;
                Console.Write("eraser");
                ModeChangeDelegate d = new ModeChangeDelegate(UpdateToolMode);
#if EXPLICIT
                if (lastButton != ButtonMode.MoveSelect)
                {
                    Invoke(d, new Object[] { ApplicationMode.StrokeErase });
                }
#else
                Invoke(d, new Object[] { ToolMode.StrokeErase });
#endif
            }
        private void RestoreEraser()
        {
            // Set the tool mode to Eraser.
            activeTool = Tool.ERASER;
            Console.Write("eraser");
            ModeChangeDelegate d = new ModeChangeDelegate(UpdateToolMode);
     #if EXPLICIT       
            if (lastButton != ButtonMode.MoveSelect){
                 Invoke(d, new Object[] { ApplicationMode.StrokeErase });
            }
#else
            Invoke(d, new Object[] { ToolMode.StrokeErase });
#endif
        }
        // triggered when a SMART Board is connected or disconnected
        private void OnSBSDKBoardStatusChange()
        {
        }

        /// <summary>
        /// Helper method that performs a hit test using the specified point.
        /// It deletes all strokes that were hit by the point
        /// </summary>
        /// <param name="pt">The point to use for hit testing</param>
        private void EraseStrokes(Point pt, Stroke currentStroke)
        {
            // Use HitTest to find the collection of strokes that are intersected
            // by the point.  The HitTestRadius constant is used to specify the 
            // radius of the hit test circle in ink space coordinates (1 unit = .01mm).
            Strokes strokesHit = myInkCollector.Ink.HitTest(pt, eraseHitTestRadius);

            if (null != currentStroke && strokesHit.Contains(currentStroke))
            {
                strokesHit.Remove(currentStroke);
            }

            // Delete all strokes that were hit by the point
            if (strokesHit.Count > 0)
            {
                EraseStrokes(strokesHit);
            }
        }

        private void EraseStrokes(Strokes strokes)
        {
            try
            {
                foreach (Stroke s in strokes)
                {
                    clusterManager.RemoveFromCluster(s);
                }
            }
            catch
            {
                Console.WriteLine("trouble removing from clusters");
            }
            try
            {
                myInkCollector.Ink.DeleteStrokes(strokes);
            }
            catch
            {
                Console.WriteLine("trouble deleting strokes");
            }
            // Recalculate bounding boxes based on new positions.
            RecalculateBoundingBoxes();

            // Repaint the screen to reflect the change
            Refresh();
        }

        private ArrayList SubSample(ArrayList inputPoints, int factor)
        {
            ArrayList outputPoints = new ArrayList();
            int count = 0;
            foreach (Point p in inputPoints)
            {
                if (count % factor == 0)
                {
                    outputPoints.Add(p);
                }
                count++;
            }
            return outputPoints;
        }

        private ArrayList RemoveDuplicates(ArrayList inputPoints)
        {
            ArrayList outputArray = new ArrayList();

            Point lastPoint = new Point();

            foreach (Point p in inputPoints) {
                if (p.X != lastPoint.X ||
                    p.Y != lastPoint.Y) {
                        outputArray.Add(p);
                }
                lastPoint = p;
            }
            return outputArray;


            //int i;
            //Point lastPoint = new Point();
            //int count;
            //for (i = 0; i < inputPoints.Length; i++) {
            //    if (inputPoints[i].X == lastPoint.X &&
            //        inputPoints[i].Y == lastPoint.Y) {
             //       continue;
             //   }
             //   count++;
           // }
//#endif
 //           Point[] output = new Point[count];

        }

        private void OnStroke(object sender, InkCollectorStrokeEventArgs e)
        {

            // this should ALWAYS be true... need to clean up code event models
            if (applicationMode == ToolMode.Ink)
            {
                // Set the timestamp for the stroke.
                Timestamp.SetTimestampForStroke(e.Stroke);

                clusterManager.ClusterNewStroke(e.Stroke);

                // Recalculate bounding boxes based on new positions.
                RecalculateBoundingBoxes();
            }
            else if (applicationMode == ToolMode.Selection)
            {
                // This stroke corresponds to the lasso - 
                // cancel it so that it is not added into the ink
                e.Cancel = true;

                Strokes hitStrokes = null;

                // If there are enough lasso points, perform a hit test
                // to determine which strokes were selected. 
                if (lassoPoints.Count > 2)
                {
     
                    // removing duplicate points, and subsampling 4/5 of the 
                    // points fixes a problem with lasso'ing where the lasso selects nothing. 
                    // - dakers

                    lassoPoints = RemoveDuplicates(lassoPoints);

                    lassoPoints = SubSample(lassoPoints, 5);
    
                    // Convert the lasso points from pixels to ink space
                    Point[] inkLassoPoints = (Point[])lassoPoints.ToArray(typeof(Point));
                    //myInkCollector.Renderer.PixelToInkSpace(CreateGraphics(), ref inkLassoPoints);
                    myInkCollector.Renderer.PixelToInkSpace(writingPane.CreateGraphics(), ref inkLassoPoints);

                    // Perform a hit test on this ink collector's ink to
                    // determine which points were selected by the lasso stroke.
                    //
                    // Note that there is a slight inefficiency here since the
                    // lasso stroke is part of the ink and, therefore, part of the
                    // hit test - even though we don't need it.   It would have 
                    // been more efficient to remove the stroke from the ink before 
                    // calling HitTest.  However, it is not good practice to modify 
                    // the stroke inside of its own event handler.
                    
                    hitStrokes = myInkCollector.Ink.HitTest(inkLassoPoints, LassoPercent);
                    hitStrokes.Remove(e.Stroke);

                }

                // Reset the lasso points
                lassoPoints.Clear();
                lastDrawnLassoDot = Point.Empty;

                // Use helper method to set the selection
                SetSelection(hitStrokes);
            }

            //Refresh();
        }

        private Rectangle[] GetBoundingBoxes(ICollection clusters, int inflate)
        {
            // Declare the array of rectangles to hold the result
            Rectangle[] clusterRects;

            // If there is at least one unit, we construct the rectangles
            if ((null != clusters) && (0 < clusters.Count))
            {
                // We need to convert rectangles from ink units to
                // pixel units. For that, we need Graphics object
                // to pass to InkRenderer.InkSpaceToPixel method
                using (Graphics g = writingPane.CreateGraphics())
                {

                    // Construct the rectangles
                    clusterRects = new Rectangle[clusters.Count];

                    // InkRenderer.InkSpaceToPixel takes Point as parameter. 
                    // Create two Point objects to point to (Top, Left) and
                    // (Width, Height) properties of ractangle. (Width, Height)
                    // is used instead of (Right, Bottom) because (Right, Bottom)
                    // are read-only properties on Rectangle
                    Point ptLocation = new Point();
                    Point ptSize = new Point();

                    // Index into the bounding boxes
                    int i = 0;

                    // Iterate through the collection of division units to obtain the bounding boxes
                    foreach (Strokes cluster in clusters)
                    {
                        //Console.WriteLine("Getting bounding box for cluster " + i);
                        // Get the bounding box of the strokes of the division unit
                        clusterRects[i] = cluster.GetBoundingBox();

                        // The bounding box is in ink space unit. Convert them into pixel unit. 
                        ptLocation = clusterRects[i].Location;
                        ptSize.X = clusterRects[i].Width;
                        ptSize.Y = clusterRects[i].Height;

                        // Convert the Location from Ink Space to Pixel Space
                        myInkCollector.Renderer.InkSpaceToPixel(g, ref ptLocation);

                        // Convert the Size from Ink Space to Pixel Space
                        myInkCollector.Renderer.InkSpaceToPixel(g, ref ptSize);

                        // Assign the result back to the corresponding properties
                        clusterRects[i].Location = ptLocation;
                        clusterRects[i].Width = ptSize.X;
                        clusterRects[i].Height = ptSize.Y;

                        // Inflate the rectangle by inflate pixels in both directions
                        clusterRects[i].Inflate(inflate, inflate);

                        // Increment the index
                        ++i;
                    }

                } // Relinquish the Graphics object
            }
            else
            {
                // Otherwise we return null
                clusterRects = null;
            }

            // Return the Rectangle[] object
            return clusterRects;
        }

        private void miErase_Click(object sender, EventArgs e)
        {
            UpdateToolMode(ToolMode.StrokeErase);
        }

        /// <summary>
        /// Helper method to update the application mode
        /// </summary>
        /// <param name="newMode">The new mode</param>
        private void UpdateToolMode(ToolMode newMode)
        {
            // Update the state of the Select, Ink, and Erase menu items
            miInk.Checked = (ToolMode.Ink == newMode);
            miErase.Checked = (ToolMode.StrokeErase == newMode);
            miSelect.Checked = (ToolMode.Selection == newMode) || (ToolMode.MoveInk == newMode);

            // Do other mode-specific init
            try
            {
                if (newMode == ToolMode.Selection)
                {
                    myInkCollector.Enabled = true;

                    // Lasso strokes should be transparent 
                    myInkCollector.DefaultDrawingAttributes.Transparency = 255;

                    // Clear the selection
                    SetSelection(null);
                }
                else if (newMode == ToolMode.StrokeErase)
                {
                    // Turn off ink collector while erasing
                    myInkCollector.Enabled = false;

                    // Set the tool mode to Eraser.
                    activeTool = Tool.ERASER;

                    // Clear the selection
                    SetSelection(null);
                }
                else if (newMode == ToolMode.Ink)
                {
                    myInkCollector.Enabled = true;

                    // Set the tool mode to black pen, if it's not already a pen.
                    if (activeTool == Tool.ERASER || activeTool == Tool.NONE)
                    {
                        activeTool = Tool.BLACK_PEN;
                    }

                    // Ink strokes should be opaque
                    myInkCollector.DefaultDrawingAttributes.Transparency = 0;

                    // Clear the selection
                    SetSelection(null);
                }
                else if (newMode == ToolMode.MoveInk)
                {
                    // do nothing
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message);
            }

            applicationMode = newMode;
            Console.WriteLine("*** Mode is now : " + applicationMode);
            Console.WriteLine("*** Ink Collected enabled? : " + myInkCollector.Enabled);

            //Invalidate();
            //Refresh();
        }

        private void DrawClusters(Graphics g)
        {
            if (null != clusterBoundingBoxes)
            {
                Color newColor = Color.FromArgb(clusterAlpha, Color.Silver);
                Pen penBox = new Pen(newColor, 2);
                penBox.DashPattern = new float[] { 4.0F, 2.0F, 1.0F, 3.0F };
                g.DrawRectangles(penBox, clusterBoundingBoxes);
            }
        }

        private void RecalculateBoundingBoxes()
        {
            clusterBoundingBoxes = GetBoundingBoxes(clusterManager.GetClusters(), 1);
        }

        private Strokes StrokesNearMousePointForSelection(int x, int y)
        {
            Point pt = new Point(x, y);

            // Convert the specified point from pixel to ink space coordinates
            myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref pt);

            // Return any strokes nearby
            Strokes strokesHit = myInkCollector.Ink.HitTest(pt, selectHitTestRadius);
            return strokesHit;
        }

        public void SimulateRangerEvent(ZONES zone)
        {
            float[] rValues = { 0, 0, 0, 0 };
            RangerEvent e = new RangerEvent(zone, 0, rValues);
            enteredZone(e);
        }

        private void ScrollValueChanged(object sender, EventArgs e)
        {
            JumpToScreen(hScrollBar1.Value);
        }

        private void JumpToScreen(int newScreenNumber)
        {
            Console.WriteLine("*** Calling JumpToScreen *** " + newScreenNumber + "," + currentScreenNumber);
            if (newScreenNumber == currentScreenNumber)
            {
                // showing that page, do nothing
                return;
            }
            if (newScreenNumber < 0)
            {
                // on the first page, do nothing
                return;
            }
            if (newScreenNumber > hScrollBar1.Maximum)
            {
                // on the last page, do nothing
                return;
            }
            int delta = newScreenNumber - currentScreenNumber;
            MoveAllStrokes(-delta * writingPane.Width, 0);
            currentScreenNumber = newScreenNumber;
            hScrollBar1.Value = newScreenNumber;
        }

        private void NextScreen()
        {
            JumpToScreen(currentScreenNumber + 1);
        }

        private void PreviousScreen()
        {
            JumpToScreen(currentScreenNumber - 1);
        }

        private void NewPage()
        {
            // creating a new page, increment the scrollbar
            hScrollBar1.Maximum += 1;

            // and jump to new page
            JumpToScreen(hScrollBar1.Maximum);

            // and show the new thumbs
            UpdatePageThumbnails(true);

            //Matrix transform = new Matrix();
            //myInkCollector.Renderer.GetViewTransform(ref transform);
            //Point distance = new Point(50, 0);
            //myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref distance);
            //transform.Translate(distance.X, distance.Y);
            //myInkCollector.Renderer.SetViewTransform(transform);
            //writingPane.Invalidate();
        }

        private void newPageButton_Click(object sender, EventArgs e)
        {
            //lastButton = ButtonMode.NewPage;
            NewPage();
        }

        private void awarenessButton_Click(object sender, EventArgs e)
        {
            //lastButton = ButtonMode.Awareness;
            SimulateRangerEvent(ZONES.FARTHEST);
        }

        private void moveSelectButton_Click(object sender, EventArgs e)
        {
            lastButton = ButtonMode.MoveSelect;
            SimulateRangerEvent(ZONES.CLOSE);

        }

        private void writeButton_Click(object sender, EventArgs e)
        {
            lastButton = ButtonMode.Write;
            SimulateRangerEvent(ZONES.CLOSEST);
        }


        private void miShowHideToolbarButton_Click(object sender, EventArgs e)
        {
            //HACK ALERT: this way of holding the show/hide state should be done better.
            if (inkSizeToolstrip.Size.Width > 150)
            {
                //collapse toolbar
                inkSizeToolstrip.Size = new System.Drawing.Size(110,inkSizeToolstrip.Size.Height);
                //change text on button
                miShowHideToolbarButton.Text = "Show Ink Sizes";
            }
            else
            {
                inkSizeToolstrip.Size = new System.Drawing.Size(275, inkSizeToolstrip.Size.Height);
                miShowHideToolbarButton.Text = "Hide Ink Sizes";
            }
        }
        private void ToggleExplicitUI()
        {
            if (showexplicitUI) showexplicitUI = false;
            else showexplicitUI = true;
            ShowExplicitUI(showexplicitUI);
        }
        private void ShowExplicitUI(bool visible)
        {
            this.inkSizeToolstrip.Visible = visible;
            this.writeButton.Visible = visible;
            this.moveSelectButton.Visible = visible;
            this.awarenessButton.Visible = visible;
            this.newPageButton.Visible = visible;
            this.newPageButton.Visible = visible;
            this.saveButton.Visible = visible;
        }

        // Right now, this function only shows top left corner, will scale soon
        private Image GetThumbnailForPage(int page, int width, int height)
        {
            // Copy the strokes from the current strokes object
            Strokes strokes = myInkCollector.Ink.CreateStrokes();
            strokes.Add(myInkCollector.Ink.Strokes);

            // Translate them the right amount in view
            int delta = page - currentScreenNumber;
            Point p = new Point(-delta * writingPane.Width, 0);
            myInkCollector.Renderer.PixelToInkSpace(this.Handle, ref p);
            Matrix transform = new Matrix();
            Matrix origTransform = new Matrix();
            myInkCollector.Renderer.GetViewTransform(ref transform);
            myInkCollector.Renderer.GetViewTransform(ref origTransform);
            transform.Scale(0.09f, 0.09f);
            transform.Translate(p.X, p.Y);
            myInkCollector.Renderer.SetViewTransform(transform);

            // Create a new bitmap and draw into it
            Bitmap thumbnail = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(thumbnail);
            g.Clear(Color.LightGray);
            myInkCollector.Renderer.Draw(g, strokes);

            // Restore the old view
            myInkCollector.Renderer.SetViewTransform(origTransform);

            return thumbnail;
        }

        private void UpdatePageThumbnails(bool visible)
        {
            foreach (PictureBox box in boxList)
            {
                this.Controls.Remove(box);
            }
            boxList = new ArrayList();

            // for every thumbnail in list, update its location
            for (int i = 0; i <= hScrollBar1.Maximum; i++)
            {
                PictureBox box;
                float scrollbarWidth = writingPane.Width - 16 * 2;
                float pageScrollbarWidth = scrollbarWidth / (float)(hScrollBar1.Maximum + 1);
                if (thumbnailWidth > pageScrollbarWidth - 4)
                {
                    thumbnailWidth = (int)Math.Max(10, pageScrollbarWidth - 4);
                    thumbnailHeight = thumbnailWidth;
                }
                Image thumbnail = GetThumbnailForPage(i, thumbnailWidth, thumbnailHeight);
                box = new PictureBox();
                box.Image = thumbnail;
                box.Name = "thumbnail" + i;
                box.Visible = false;
                this.Controls.Add(box);
                box.BringToFront();
                box.Size = new Size(thumbnailWidth, thumbnailHeight);
                // in reality, always scale from original size image; this is just test code
                //box.Image = ImageUtil.ScaleToFixedSize(box.Image, thumbnailWidth, thumbnailHeight);
                box.Location = new Point(
                    (int)((i + 0.5f) * pageScrollbarWidth) - thumbnailWidth / 2 + 16, 
                    hScrollBar1.Location.Y - thumbnailHeight - 5
                );
                boxList.Add(box);
            }
            foreach (PictureBox box in boxList)
            {
                box.Visible = visible;
            }
        }

        public WritingPane GetWritingPane()
        {
            return writingPane;
        }

        private void ScaleButtons(float percentage)
        {
            // button size 85 x 47 by default
            // position of bottom (save) button: 695, 385
            int defaultWidth = 85;
            int defaultHeight = 47;
            int scaledWidth = (int)(defaultWidth * percentage);
            int scaledHeight = (int)(defaultHeight * percentage);
            Size s = new Size(scaledWidth, defaultHeight);
            writeButton.Size = s;
            writeButton.Location = new Point(695 + defaultWidth - scaledWidth, writeButton.Location.Y);
            moveSelectButton.Size = s;
            moveSelectButton.Location = new Point(695 + defaultWidth - scaledWidth, moveSelectButton.Location.Y);
            awarenessButton.Size = s;
            awarenessButton.Location = new Point(695 + defaultWidth - scaledWidth, awarenessButton.Location.Y);
            newPageButton.Size = s;
            newPageButton.Location = new Point(695 + defaultWidth - scaledWidth, newPageButton.Location.Y);
            saveButton.Size = s;
            saveButton.Location = new Point(695 + defaultWidth - scaledWidth, saveButton.Location.Y);
        }

    }
}
